diff --git a/tests/strategy_switch_worker_validation.mjs b/tests/strategy_switch_worker_validation.mjs index 0472b0e..75fc2c7 100644 --- a/tests/strategy_switch_worker_validation.mjs +++ b/tests/strategy_switch_worker_validation.mjs @@ -62,6 +62,13 @@ assert.ok(indexHtml.includes('id="runtime-authority-status"')); assert.ok(indexHtml.includes('id="control-plane-view-button"')); assert.ok(indexHtml.includes('id="control-plane-view"')); assert.ok(indexHtml.includes('id="control-plane-list"')); +assert.ok(indexHtml.includes('id="m0-research-notice"')); +assert.ok(indexHtml.includes('id="m0-research-list"')); +assert.ok(indexHtml.includes('
\n ')); +assert.ok(indexHtml.includes('function renderM0Research()')); +assert.ok(indexHtml.includes('requestJson("/api/m0-research")')); +assert.ok(indexHtml.includes('const M0_RESEARCH_DISPLAY_LIMIT = 100;')); +assert.ok(indexHtml.includes('entries.slice(0, M0_RESEARCH_DISPLAY_LIMIT)')); assert.ok(indexHtml.includes('id="adaptive-selection-list"')); assert.ok(indexHtml.includes('id="adaptive-selection-notice"')); assert.ok(indexHtml.includes('data-i18n="planEyebrow"')); diff --git a/web/strategy-switch-console/README.zh-CN.md b/web/strategy-switch-console/README.zh-CN.md index 7a4185d..86a1d47 100644 --- a/web/strategy-switch-console/README.zh-CN.md +++ b/web/strategy-switch-console/README.zh-CN.md @@ -147,6 +147,12 @@ validator。来源已标为 stale 的观测不会因读取而被提升为 fresh `permitted_next_step=research_validation_only`;将 M0 线索转为 P1--P3 研究任务仍需独立的 证据绑定与准入流程。 +管理台会在“今日待处理 / 只读”页将 M0 放在默认折叠的“研究观察”区域;它只请求 +`GET /api/m0-research`。区域最多显示 100 条观察,并仅展示研究对象、状态、新鲜度、观察期、 +来源置信/风格、当前周期分歧、历史失效漂移、来源摘要和本次查看时间。接口不可用、来源缺失或 +数据过期时保持安全空态或历史提示,不补造结论。该区域没有写入、切换或运行控制,也不会显示 +买卖、权重、策略、平台或账户信息;固定边界仍是“仅研究、无订单”。 + ## 文件结构 ```text diff --git a/web/strategy-switch-console/app.js b/web/strategy-switch-console/app.js index 3cceb9b..ec6b2a1 100644 --- a/web/strategy-switch-console/app.js +++ b/web/strategy-switch-console/app.js @@ -860,6 +860,19 @@ executionEvidenceDetail: "策略:{strategy} · 数据:{data} · 执行:{execution} · Shadow:{shadow} · Paper:{paper}", executionEvidenceNoOrder: "固定边界:只读证据;不包含账户、订单、资金或 P6 实盘授权。", executionEvidenceNext: "下一步", + m0ResearchBoard: "按需查看 M0 研究观察", + m0ResearchLoginNotice: "登录后读取私有 M0 研究台账;缺失台账不会被补成研究结论。", + m0ResearchStaleNotice: "M0 研究观察已超过有效期,仅保留为历史上下文。", + m0ResearchUnavailableNotice: "还没有可用的 M0 研究台账;页面保持 fail-closed 空状态。", + m0ResearchUpstreamNotice: "M0 研究台账已加载,但有 {count} 个上游提示;页面不会补造研究结论。", + m0ResearchEmpty: "暂无可展示的已验证 M0 研究观察。", + m0ResearchMore: "仅显示前 {count} 条研究观察;其余仍保留在只读台账中。", + m0ResearchMeta: "{kind} · 查看于:{viewed}", + m0ResearchStateFreshness: "状态:{state} · 新鲜度:{freshness}", + m0ResearchHorizons: "主要观察期:{primary} · 适用观察期:{suitable}", + m0ResearchEvidence: "置信:{confidence} · 风格:{style} · 资料摘要:{digest}", + m0ResearchConsistency: "当前周期分歧:{conflict} · 历史失效漂移:{drift}", + m0ResearchNoOrder: "固定边界:仅研究、无订单;不产生任何运行动作。", adaptiveSelectionBoard: "按需查看 M1 Shadow 建议", adaptiveSelectionLoginNotice: "登录后读取私有 Shadow 建议;缺失来源不会被补成策略或订单。", adaptiveSelectionStaleNotice: "此 Shadow 建议已超出新鲜度窗口,仅保留为历史上下文。", @@ -1129,6 +1142,19 @@ executionEvidenceDetail: "strategy: {strategy} · data: {data} · execution: {execution} · shadow: {shadow} · paper: {paper}", executionEvidenceNoOrder: "Fixed boundary: read-only evidence; no account, order, funds, or P6 live authority.", executionEvidenceNext: "NEXT", + m0ResearchBoard: "View M0 research observations on demand", + m0ResearchLoginNotice: "Sign in to read the private M0 research ledger. A missing ledger never implies a research conclusion.", + m0ResearchStaleNotice: "These M0 research observations are beyond their validity window and remain historical context only.", + m0ResearchUnavailableNotice: "No usable M0 research ledger is available yet. This view remains fail-closed and empty.", + m0ResearchUpstreamNotice: "The M0 research ledger loaded with {count} upstream notice(s); no research conclusion is invented.", + m0ResearchEmpty: "No verified M0 research observation is available to display.", + m0ResearchMore: "Showing the first {count} research observations; the remainder stays in the read-only ledger.", + m0ResearchMeta: "{kind} · viewed {viewed}", + m0ResearchStateFreshness: "State: {state} · freshness: {freshness}", + m0ResearchHorizons: "Primary horizon: {primary} · suitable horizons: {suitable}", + m0ResearchEvidence: "Confidence: {confidence} · style: {style} · source digest: {digest}", + m0ResearchConsistency: "Current horizon conflict: {conflict} · historical stale drift: {drift}", + m0ResearchNoOrder: "Fixed boundary: research only, no order, and no runtime action.", adaptiveSelectionBoard: "View M1 Shadow suggestions on demand", adaptiveSelectionLoginNotice: "Sign in to read private Shadow suggestions. Missing sources never imply a strategy or order.", adaptiveSelectionStaleNotice: "This Shadow suggestion is beyond its freshness window and remains historical context only.", @@ -1409,6 +1435,24 @@ candidates: [], errors: [], }, + m0Research: { + payload: { + data_status: "unavailable", + viewed_at: null, + summary: { + subject_count: 0, + observation_count: 0, + fresh_observation_count: 0, + stale_observation_count: 0, + unknown_observation_count: 0, + horizon_conflict_count: 0, + historical_stale_horizon_drift_count: 0, + }, + subjects: [], + policy: { authority: "research_only", no_order: true, permitted_next_step: "research_validation_only" }, + errors: [], + }, + }, adaptiveSelection: { payload: { data_status: "unavailable", @@ -3720,6 +3764,190 @@ } } + const M0_RESEARCH_DISPLAY_LIMIT = 100; + + const m0ResearchLabels = { + subject: { + asset_idea: { zh: "资产观察", en: "Asset idea" }, + theme_context: { zh: "主题背景", en: "Theme context" }, + strategy_hypothesis: { zh: "研究假设", en: "Research hypothesis" }, + risk_context: { zh: "风险背景", en: "Risk context" }, + }, + state: { + candidate: { zh: "待验证", en: "Candidate" }, + source_verification_required: { zh: "等待来源核验", en: "Source verification required" }, + deferred: { zh: "暂缓", en: "Deferred" }, + context_only: { zh: "仅作背景", en: "Context only" }, + }, + freshness: { + fresh: { zh: "有效", en: "Fresh" }, + stale: { zh: "已过期", en: "Stale" }, + unknown: { zh: "未知", en: "Unknown" }, + }, + horizon: { + short: { zh: "短期", en: "Short" }, + medium: { zh: "中期", en: "Medium" }, + long: { zh: "长期", en: "Long" }, + not_applicable: { zh: "不适用", en: "Not applicable" }, + }, + confidence: { + high: { zh: "高", en: "High" }, + medium: { zh: "中", en: "Medium" }, + low: { zh: "低", en: "Low" }, + mixed: { zh: "混合", en: "Mixed" }, + no_event: { zh: "无事件", en: "No event" }, + unknown: { zh: "未知", en: "Unknown" }, + }, + style: { + event_driven: { zh: "事件驱动", en: "Event driven" }, + long_horizon_growth: { zh: "长期成长", en: "Long-horizon growth" }, + value_quality: { zh: "价值质量", en: "Value quality" }, + macro_context: { zh: "宏观背景", en: "Macro context" }, + mixed_research: { zh: "综合研究", en: "Mixed research" }, + }, + conflict: { + none: { zh: "无", en: "None" }, + conflict: { zh: "存在", en: "Present" }, + unavailable: { zh: "不可用", en: "Unavailable" }, + }, + drift: { + none: { zh: "无", en: "None" }, + drift: { zh: "存在", en: "Present" }, + unavailable: { zh: "不可用", en: "Unavailable" }, + }, + }; + + function m0ResearchLabel(group, value) { + const entry = m0ResearchLabels[group]?.[value]; + return entry ? (state.lang === "zh" ? entry.zh : entry.en) : "—"; + } + + function m0ResearchTimestamp(value) { + if (typeof value !== "string" || !value) return "—"; + const parsed = new Date(value); + return Number.isNaN(parsed.getTime()) ? "—" : parsed.toLocaleString(); + } + + function m0ResearchHorizons(values) { + if (!Array.isArray(values) || !values.length) return "—"; + const labels = values.map((value) => m0ResearchLabel("horizon", value)).filter((value) => value !== "—"); + return labels.length ? labels.join(" / ") : "—"; + } + + function m0ResearchEntries(subjects) { + if (!Array.isArray(subjects)) return []; + const entries = []; + for (const item of subjects) { + if (!item || typeof item !== "object" || !item.subject || typeof item.subject !== "object") continue; + if (!Array.isArray(item.observations)) continue; + for (const observation of item.observations) { + if (!observation || typeof observation !== "object" || !observation.research_context) continue; + entries.push({ + subject: item.subject, + observation, + horizonConflict: item.horizon_conflict, + historicalStaleHorizonDrift: item.historical_stale_horizon_drift, + }); + } + } + return entries; + } + + function normalizeM0ResearchPayload(payload) { + if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("invalid M0 research payload"); + const subjects = Array.isArray(payload.subjects) ? payload.subjects : []; + return { + data_status: ["ready", "stale", "unavailable"].includes(payload.data_status) ? payload.data_status : "unavailable", + viewed_at: typeof payload.viewed_at === "string" ? payload.viewed_at : null, + summary: payload.summary && typeof payload.summary === "object" ? payload.summary : {}, + subjects: subjects.filter((item) => item && typeof item === "object" + && item.subject && typeof item.subject === "object" && Array.isArray(item.observations)), + policy: payload.policy && typeof payload.policy === "object" ? payload.policy : {}, + errors: Array.isArray(payload.errors) ? payload.errors : [], + }; + } + + function renderM0Research() { + const payload = state.m0Research.payload; + const entries = m0ResearchEntries(payload.subjects); + const shownEntries = entries.slice(0, M0_RESEARCH_DISPLAY_LIMIT); + const notice = el("m0-research-notice"); + if (!state.auth.allowed) { + notice.textContent = t("m0ResearchLoginNotice"); + } else if (payload.data_status === "stale") { + notice.textContent = t("m0ResearchStaleNotice"); + } else if (payload.data_status !== "ready") { + notice.textContent = t("m0ResearchUnavailableNotice"); + } else if (payload.errors?.length) { + notice.textContent = t("m0ResearchUpstreamNotice").replace("{count}", String(payload.errors.length)); + } else { + const more = entries.length > M0_RESEARCH_DISPLAY_LIMIT + ? ` ${t("m0ResearchMore").replace("{count}", String(M0_RESEARCH_DISPLAY_LIMIT))}` + : ""; + notice.textContent = `${t("m0ResearchNoOrder")}${more}`; + } + + const list = el("m0-research-list"); + list.replaceChildren(); + if (!shownEntries.length) { + const empty = document.createElement("div"); + empty.className = "health-card__empty"; + empty.textContent = t("m0ResearchEmpty"); + list.appendChild(empty); + return; + } + for (const entry of shownEntries) { + const subject = entry.subject || {}; + const observation = entry.observation || {}; + const context = observation.research_context || {}; + const card = document.createElement("article"); + card.className = "health-card"; + const main = document.createElement("div"); + main.className = "health-card__main"; + const meta = document.createElement("div"); + meta.className = "health-card__meta"; + meta.textContent = t("m0ResearchMeta") + .replace("{kind}", m0ResearchLabel("subject", subject.kind)) + .replace("{viewed}", m0ResearchTimestamp(payload.viewed_at)); + const title = document.createElement("h4"); + title.className = "health-card__title"; + title.textContent = typeof subject.identifier === "string" ? subject.identifier : "—"; + const stateFreshness = document.createElement("p"); + stateFreshness.className = "health-card__reason"; + stateFreshness.textContent = t("m0ResearchStateFreshness") + .replace("{state}", m0ResearchLabel("state", context.state)) + .replace("{freshness}", m0ResearchLabel("freshness", observation.freshness?.status)); + const horizons = document.createElement("div"); + horizons.className = "health-card__meta"; + horizons.textContent = t("m0ResearchHorizons") + .replace("{primary}", m0ResearchLabel("horizon", context.primary_horizon)) + .replace("{suitable}", m0ResearchHorizons(context.suitable_horizons)); + const evidence = document.createElement("div"); + evidence.className = "health-card__meta"; + evidence.textContent = t("m0ResearchEvidence") + .replace("{confidence}", m0ResearchLabel("confidence", context.source_confidence)) + .replace("{style}", m0ResearchLabel("style", context.source_style)) + .replace("{digest}", `${shortResearchDigest(observation.source_report_digest)} / ${shortResearchDigest(observation.source_entry_digest)}`); + const consistency = document.createElement("div"); + consistency.className = "health-card__meta"; + consistency.textContent = t("m0ResearchConsistency") + .replace("{conflict}", m0ResearchLabel("conflict", entry.horizonConflict?.status)) + .replace("{drift}", m0ResearchLabel("drift", entry.historicalStaleHorizonDrift?.status)); + main.append(meta, title, stateFreshness, horizons, evidence, consistency); + const status = document.createElement("div"); + status.className = "health-card__score"; + const label = document.createElement("small"); + label.textContent = "M0"; + const freshness = document.createElement("strong"); + freshness.textContent = m0ResearchLabel("freshness", observation.freshness?.status); + const stateLabel = document.createElement("small"); + stateLabel.textContent = m0ResearchLabel("state", context.state); + status.append(label, freshness, stateLabel); + card.append(main, status); + list.appendChild(card); + } + } + function normalizeAdaptiveSelectionPayload(payload) { if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("invalid adaptive selection payload"); const selections = Array.isArray(payload.selections) ? payload.selections : []; @@ -4074,6 +4302,7 @@ renderRuntimeAuthorityStatus(); renderConsoleView(); renderControlPlane(); + renderM0Research(); renderAdaptiveSelection(); renderExecutionEvidence(); renderResearchTasks(); @@ -4103,6 +4332,7 @@ if (state.auth.allowed) { await refreshControlPlane(); await refreshOwnerDecisions(); + await refreshM0Research(); await refreshAdaptiveSelection(); await refreshExecutionEvidence(); await refreshResearchTasks(); @@ -4173,6 +4403,34 @@ renderControlPlane(); } + async function refreshM0Research() { + if (!state.auth.allowed) { + renderM0Research(); + return; + } + try { + state.m0Research.payload = normalizeM0ResearchPayload(await requestJson("/api/m0-research")); + } catch { + state.m0Research.payload = { + data_status: "unavailable", + viewed_at: null, + summary: { + subject_count: 0, + observation_count: 0, + fresh_observation_count: 0, + stale_observation_count: 0, + unknown_observation_count: 0, + horizon_conflict_count: 0, + historical_stale_horizon_drift_count: 0, + }, + subjects: [], + policy: { authority: "research_only", no_order: true, permitted_next_step: "research_validation_only" }, + errors: ["m0_research_request_failed"], + }; + } + renderM0Research(); + } + async function refreshAdaptiveSelection() { if (!state.auth.allowed) { renderAdaptiveSelection(); @@ -4457,6 +4715,7 @@ if (state.view === "control") { refreshControlPlane(); refreshOwnerDecisions(); + refreshM0Research(); refreshResearchTasks(); } if (state.view === "health") refreshHealth(); diff --git a/web/strategy-switch-console/app_js.js b/web/strategy-switch-console/app_js.js index dc4fed5..0e3cfe5 100644 --- a/web/strategy-switch-console/app_js.js +++ b/web/strategy-switch-console/app_js.js @@ -1,2 +1,2 @@ // Generated — JS asset -export const APP_JS = "\n\n let platformMeta = {\n binance: { label: \"Binance\", code: \"BN\", accent: \"var(--bn)\" },\n firstrade: { label: \"Firstrade\", code: \"FT\", accent: \"var(--ft)\" },\n ibkr: { label: \"IBKR\", code: \"IB\", accent: \"var(--ib)\" },\n longbridge: { label: \"LongBridge\", code: \"LB\", accent: \"var(--lb)\" },\n qmt: { label: \"QMT\", code: \"QM\", accent: \"var(--qmt)\" },\n schwab: { label: \"Schwab\", code: \"SW\", accent: \"var(--sw)\" },\n };\n\n const platformRepositories = {\n binance: \"QuantStrategyLab/BinancePlatform\",\n firstrade: \"QuantStrategyLab/FirstradePlatform\",\n ibkr: \"QuantStrategyLab/InteractiveBrokersPlatform\",\n longbridge: \"QuantStrategyLab/LongBridgePlatform\",\n qmt: \"QuantStrategyLab/QmtPlatform\",\n schwab: \"QuantStrategyLab/CharlesSchwabPlatform\",\n };\n // Alias for backward compatibility\n const defaultRepositories = platformRepositories;\n\n const defaultAccountOptions = window.__DEFAULT_ACCOUNT_OPTIONS__ || {\n binance: [{\"key\": \"default\", \"label\": \"Binance\", \"target_name\": \"default\", \"cash_currency\": \"USD\", \"supported_domains\": [\"crypto\"]}],\n firstrade: [{\"key\": \"preview\", \"label\": \"Firstrade\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\", \"service_name\": \"firstrade-quant-service\"}],\n ibkr: [{\"key\": \"preview\", \"label\": \"IBKR\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\", \"hk_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\"}],\n longbridge: [{\"key\": \"preview\", \"label\": \"LongBridge\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\", \"hk_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\"}],\n qmt: [{\"key\": \"default\", \"label\": \"QMT\", \"target_name\": \"default\", \"cash_currency\": \"CNY\", \"supported_domains\": [\"cn_equity\"], \"service_name\": \"qmt-quant-service\"}],\n schwab: [{\"key\": \"preview\", \"label\": \"Schwab\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\", \"service_name\": \"charles-schwab-quant-service\"}],\n };\n\n const domainLabels = window.__DOMAIN_LABELS__ || {\n cn_equity: { zh: \"A股\", en: \"CN A-share\" },\n crypto: { zh: \"加密\", en: \"Crypto\" },\n hk_equity: { zh: \"港股\", en: \"HK Equity\" },\n us_equity: { zh: \"美股\", en: \"US Equity\" },\n };\n\n const platformConfig = window.__PLATFORM_CONFIG__ || {\n binance: {\n dry_run_only: false,\n margin_policy: false,\n reserved_cash: false,\n income_layer: false,\n option_overlay: false,\n dca: false,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n firstrade: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"firstrade-quant-service\",\n default_execution_mode: \"live\"\n },\n ibkr: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n longbridge: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n qmt: {\n dry_run_only: true,\n margin_policy: false,\n reserved_cash: false,\n income_layer: false,\n option_overlay: false,\n dca: false,\n execution_mode: \"paper\",\n service_name: \"qmt-quant-service\",\n default_execution_mode: \"paper\"\n },\n schwab: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"charles-schwab-quant-service\",\n default_execution_mode: \"live\"\n },\n };\n const runtimeAuthorityStatus = window.__QSL_RUNTIME_AUTHORITY_STATUS__ || {};\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n const reservePolicyModes = [\"none\", \"ratio\", \"floor\", \"max\"];\n const incomeLayerModes = [\"enabled\", \"disabled\"];\n const optionOverlayModes = [\"enabled\", \"disabled\"];\n const cashOnlyExecutionModes = [\"enabled\", \"disabled\"];\n const runtimeTargetModes = [\"enabled\", \"disabled\"];\n const pluginModes = [\"none\"];\n const dcaModes = [\"fixed\", \"smart\"];\n const runtimeTargetEnabledVariable = \"RUNTIME_TARGET_ENABLED\";\n const incomeLayerEnabledVariable = \"INCOME_LAYER_ENABLED\";\n const incomeLayerStartUsdVariable = \"INCOME_LAYER_START_USD\";\n const incomeLayerMaxRatioVariable = \"INCOME_LAYER_MAX_RATIO\";\n const dcaProfileDefaults = window.__DCA_PROFILE_DEFAULTS__ || {\n nasdaq_sp500_smart_dca: { defaultMode: \"fixed\", defaultBaseInvestmentUsd: \"1000\" },\n ibit_smart_dca: { defaultMode: \"fixed\", defaultBaseInvestmentUsd: \"1000\" },\n };\n const APP_BOOT_TIMEOUT_MS = 15000;\n const platformMinReservedCashVariables = {\n longbridge: \"LONGBRIDGE_MIN_RESERVED_CASH_USD\",\n ibkr: \"IBKR_MIN_RESERVED_CASH_USD\",\n schwab: \"SCHWAB_MIN_RESERVED_CASH_USD\",\n firstrade: \"FIRSTRADE_MIN_RESERVED_CASH_USD\",\n };\n const platformReservedCashRatioVariables = {\n longbridge: \"LONGBRIDGE_RESERVED_CASH_RATIO\",\n ibkr: \"IBKR_RESERVED_CASH_RATIO\",\n schwab: \"SCHWAB_RESERVED_CASH_RATIO\",\n firstrade: \"FIRSTRADE_RESERVED_CASH_RATIO\",\n };\n\n const defaultStrategyProfiles = window.__DEFAULT_STRATEGY_PROFILES__ || [\n {\n \"profile\": \"tqqq_growth_income\",\n \"label\": \"纳斯达克增长收益\",\n \"label_en\": \"NASDAQ Growth Income\",\n \"label_zh\": \"纳斯达克增长收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"250000\",\n \"income_layer_max_ratio\": \"0.55\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.3,\n \"DGRO\": 0.2,\n \"SGOV\": 0.4,\n \"SPYI\": 0.08,\n \"QQQI\": 0.02\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"tqqq_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"250000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.03\"\n },\n {\n \"profile\": \"soxl_soxx_trend_income\",\n \"label\": \"半导体趋势收益\",\n \"label_en\": \"Semiconductor Trend Income\",\n \"label_zh\": \"半导体趋势收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"150000\",\n \"income_layer_max_ratio\": \"0.95\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.15,\n \"DGRO\": 0.1,\n \"SGOV\": 0.7,\n \"SPYI\": 0.04,\n \"QQQI\": 0.01\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_income_overlay_enabled\": true,\n \"option_income_overlay_recipe\": \"soxx_put_credit_spread_income_v1\",\n \"option_income_overlay_start_usd\": \"150000\",\n \"option_income_overlay_nav_risk_ratio\": \"0.01\"\n },\n {\n \"profile\": \"nasdaq_sp500_smart_dca\",\n \"label\": \"纳指标普定投\",\n \"label_en\": \"NASDAQ/S&P 500 DCA\",\n \"label_zh\": \"纳指标普定投\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"dca_enabled\": true,\n \"dca_default_mode\": \"fixed\",\n \"dca_default_base_investment_usd\": \"1000\"\n },\n {\n \"profile\": \"ibit_smart_dca\",\n \"label\": \"IBIT比特币定投\",\n \"label_en\": \"IBIT Bitcoin DCA\",\n \"label_zh\": \"IBIT比特币定投\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"dca_enabled\": true,\n \"dca_default_mode\": \"fixed\",\n \"dca_default_base_investment_usd\": \"1000\"\n },\n {\n \"profile\": \"global_etf_rotation\",\n \"label\": \"全球ETF轮动\",\n \"label_en\": \"Global ETF Rotation\",\n \"label_zh\": \"全球ETF轮动\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"income_layer_start_usd\": \"500000\",\n \"income_layer_max_ratio\": \"0.15\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.4,\n \"DGRO\": 0.25,\n \"SGOV\": 0.3,\n \"SPYI\": 0.05\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"500000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"russell_top50_leader_rotation\",\n \"label\": \"罗素Top50领涨\",\n \"label_en\": \"Russell Top50 Leaders\",\n \"label_zh\": \"罗素Top50领涨\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"300000\",\n \"income_layer_max_ratio\": \"0.25\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.45,\n \"DGRO\": 0.3,\n \"SGOV\": 0.25\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"300000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"tecl_xlk_trend_income\",\n \"label\": \"TECL/XLK趋势收益\",\n \"label_en\": \"TECL/XLK Trend Income\",\n \"label_zh\": \"TECL/XLK趋势收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"failed_promotion_vs_live_profiles\"\n },\n {\n \"profile\": \"us_equity_combo\",\n \"label\": \"美股核心组合\",\n \"label_en\": \"US Core Combo\",\n \"label_zh\": \"美股核心组合\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\",\n \"income_layer_start_usd\": \"300000\",\n \"income_layer_max_ratio\": \"0.25\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.25,\n \"DGRO\": 0.25,\n \"SGOV\": 0.2,\n \"SPYI\": 0.15,\n \"QQQI\": 0.15\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"300000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"us_equity_combo_core\",\n \"label\": \"美股核心组合影子\",\n \"label_en\": \"US Core Combo Shadow\",\n \"label_zh\": \"美股核心组合影子\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"us_equity_combo_leveraged\",\n \"label\": \"美股加速组合\",\n \"label_en\": \"US Alpha Combo\",\n \"label_zh\": \"美股加速组合\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"hk_global_etf_tactical_rotation\",\n \"label\": \"港股ETF战术轮动\",\n \"label_en\": \"HK ETF Tactical Rotation\",\n \"label_zh\": \"港股ETF战术轮动\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"hk_low_vol_dividend_quality_snapshot\",\n \"label\": \"港股红利质量\",\n \"label_en\": \"HK Dividend Quality\",\n \"label_zh\": \"港股红利质量\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"hk_equity_combo\",\n \"label\": \"港股恒生组合\",\n \"label_en\": \"HK Core Combo\",\n \"label_zh\": \"港股恒生组合\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"cn_industry_etf_rotation\",\n \"label\": \"A股行业ETF轮动\",\n \"label_en\": \"CN Industry ETF Rotation\",\n \"label_zh\": \"A股行业ETF轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"cn_industry_etf_rotation_aggressive\",\n \"label\": \"A股ETF轮动\",\n \"label_en\": \"CN ETF Rotation\",\n \"label_zh\": \"A股ETF轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"live_candidate\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"live_candidate_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_index_etf_tactical_rotation\",\n \"label\": \"A股宽基ETF战术轮动\",\n \"label_en\": \"CN Index ETF Tactical Rotation\",\n \"label_zh\": \"A股宽基ETF战术轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_tactical_rotation\",\n \"label\": \"创业板战术轮动\",\n \"label_en\": \"CN ChiNext Tactical Rotation\",\n \"label_zh\": \"创业板战术轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_growth_momentum_quality\",\n \"label\": \"创业板成长动量质量\",\n \"label_en\": \"CN ChiNext Growth Momentum Quality\",\n \"label_zh\": \"创业板成长动量质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_dividend_quality_snapshot\",\n \"label\": \"A股红利质量\",\n \"label_en\": \"CN Dividend Quality\",\n \"label_zh\": \"A股红利质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_growth_momentum_quality_snapshot\",\n \"label\": \"创业板成长质量快照\",\n \"label_en\": \"CN ChiNext Growth Quality Snapshot\",\n \"label_zh\": \"创业板成长质量快照\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_star_growth_momentum_quality\",\n \"label\": \"科创板成长动量质量\",\n \"label_en\": \"CN STAR Growth Momentum Quality\",\n \"label_zh\": \"科创板成长动量质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_equity_combo\",\n \"label\": \"A股进取组合\",\n \"label_en\": \"CN Alpha Combo\",\n \"label_zh\": \"A股进取组合\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"crypto_live_pool_rotation\",\n \"label\": \"加密实时池轮动\",\n \"label_en\": \"Crypto Live Pool Rotation\",\n \"label_zh\": \"加密实时池轮动\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"crypto_btc_dca\",\n \"label\": \"BTC定投\",\n \"label_en\": \"BTC DCA\",\n \"label_zh\": \"BTC定投\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\"\n },\n {\n \"profile\": \"crypto_trend_rotation\",\n \"label\": \"山寨趋势轮动\",\n \"label_en\": \"Altcoin Trend\",\n \"label_zh\": \"山寨趋势轮动\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"crypto_equity_combo\",\n \"label\": \"加密动量组合\",\n \"label_en\": \"Crypto Core Combo\",\n \"label_zh\": \"加密动量组合\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n }\n ];\n\n const localStrategyLabels = {\n tqqq_growth_income: { zh: \"纳斯达克增长收益\", en: \"NASDAQ Growth Income\" },\n soxl_soxx_trend_income: { zh: \"半导体趋势收益\", en: \"Semiconductor Trend Income\" },\n nasdaq_sp500_smart_dca: { zh: \"纳指标普定投\", en: \"NASDAQ/S&P 500 DCA\" },\n ibit_smart_dca: { zh: \"IBIT比特币定投\", en: \"IBIT Bitcoin DCA\" },\n global_etf_rotation: { zh: \"全球ETF轮动\", en: \"Global ETF Rotation\" },\n russell_top50_leader_rotation: { zh: \"罗素Top50领涨\", en: \"Russell Top50 Leaders\" },\n hk_global_etf_tactical_rotation: { zh: \"港股ETF战术轮动\", en: \"HK ETF Tactical Rotation\" },\n hk_low_vol_dividend_quality_snapshot: { zh: \"港股红利质量\", en: \"HK Dividend Quality\" },\n cn_industry_etf_rotation: { zh: \"A股行业ETF轮动\", en: \"CN Industry ETF Rotation\" },\n cn_dividend_quality_snapshot: { zh: \"A股红利质量\", en: \"CN Dividend Quality\" },\n us_equity_combo: { zh: \"美股核心组合\", en: \"US Core Combo\" },\n us_equity_combo_leveraged: { zh: \"美股加速组合\", en: \"US Alpha Combo\" },\n hk_equity_combo: { zh: \"港股恒生组合\", en: \"HK Core Combo\" },\n cn_industry_etf_rotation_aggressive: { zh: \"A股ETF轮动\", en: \"CN ETF Rotation\" },\n cn_stock_momentum_rotation: { zh: \"A股个股动量\", en: \"CN Stock Momentum\" },\n cn_equity_combo: { zh: \"A股进取组合\", en: \"CN Alpha Combo\" },\n crypto_btc_dca: { zh: \"BTC定投\", en: \"BTC DCA\" },\n crypto_trend_rotation: { zh: \"山寨趋势轮动\", en: \"Altcoin Trend\" },\n crypto_equity_combo: { zh: \"加密动量组合\", en: \"Crypto Core Combo\" },\n };\n\n const fallbackIncomeLayerDefaults = window.__INCOME_LAYER_DEFAULTS__ || {\n tqqq_growth_income: {\n startUsd: 250000,\n maxRatio: \"0.55\",\n allocations: { SCHD: 0.30, DGRO: 0.20, SGOV: 0.40, SPYI: 0.08, QQQI: 0.02 },\n },\n soxl_soxx_trend_income: {\n startUsd: 150000,\n maxRatio: \"0.95\",\n allocations: { SCHD: 0.15, DGRO: 0.10, SGOV: 0.70, SPYI: 0.04, QQQI: 0.01 },\n },\n global_etf_rotation: {\n startUsd: 500000,\n maxRatio: \"0.15\",\n allocations: { SCHD: 0.40, DGRO: 0.25, SGOV: 0.30, SPYI: 0.05 },\n },\n russell_top50_leader_rotation: {\n startUsd: 300000,\n maxRatio: \"0.25\",\n allocations: { SCHD: 0.45, DGRO: 0.30, SGOV: 0.25 },\n },\n us_equity_combo: {\n startUsd: 300000,\n maxRatio: \"0.25\",\n allocations: { SCHD: 0.25, DGRO: 0.25, SGOV: 0.20, SPYI: 0.15, QQQI: 0.15 },\n }};\n let incomeLayerDefaults = {};\n const fallbackOptionOverlayDefaults = window.__OPTION_OVERLAY_DEFAULTS__ || {\n tqqq_growth_income: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"tqqq_leaps_growth_v1\", startUsd: \"250000\", ratio: \"0.03\", ratioKind: \"budget\" },\n ],\n },\n soxl_soxx_trend_income: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"income\", recipe: \"soxx_put_credit_spread_income_v1\", startUsd: \"150000\", ratio: \"0.01\", ratioKind: \"risk\" },\n ],\n },\n global_etf_rotation: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"500000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n russell_top50_leader_rotation: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"300000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n us_equity_combo: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"300000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n us_equity_combo_leveraged: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [],\n }};\n let optionOverlayDefaults = {};\n\n const strategyDomains = [\"us_equity\", \"hk_equity\", \"cn_equity\", \"crypto\"];\n let strategyOptions = [];\n let strategyLabels = {};\n let strategyCatalog = {};\n\n\n const copy = {\n zh: {\n appTitle: \"QuantStrategyLab\",\n appSubtitle: \"已验证证据、运行许可和所有者决策。\",\n controlPlaneView: \"待处理\",\n healthView: \"运行保障\",\n switchView: \"运行计划\",\n controlPlaneEyebrow: \"今日待处理 / 只读\",\n controlPlaneTitle: \"只处理需要你决定的事。\",\n controlPlaneSubtitle: \"这里不是交易大屏:只显示经验证的待办、证据是否足够,以及需要人工做出的边界决定。\",\n controlCandidateTotal: \"候选总数\",\n controlDeferred: \"本次延期\",\n controlParked: \"已停车\",\n controlOwnerDecision: \"等待我决定\",\n controlCandidateBoard: \"需要跟进\",\n controlDataReady: \"快照已同步(不代表策略可运行)\",\n controlDataStale: \"快照已过期\",\n controlDataUnavailable: \"等待可用快照\",\n controlAttentionResearchOnly: \"仅研究,未授予执行权限\",\n controlAttentionRequired: \"研究状态需要关注\",\n controlAttentionUnavailable: \"尚无已验证研究状态\",\n controlComputedAt: \"最近计算:{time}\",\n controlLoginNotice: \"登录后读取私有全局快照;没有快照时不会展示虚构信息。\",\n controlStaleNotice: \"全局快照已超过允许的新鲜度窗口;页面保留原始记录,但不把它作为当前结论。\",\n controlUnavailableNotice: \"还没有可用的全局快照;当前页面保持 fail-closed 空状态。\",\n controlUpstreamNotice: \"快照已加载,但有 {count} 个上游提示;缺失信息不会被补成推断结论。\",\n controlAttentionNotice: \"研究状态需要关注(延期 {deferred}、停车 {parked}、信号 {signals})。快照送达不等于策略验证或执行授权;系统保持不交易。\",\n controlEmptyCandidates: \"暂无可展示的候选快照。\",\n controlNoRecommendation: \"没有可用的机器建议。\",\n controlStageMeta: \"阶段:{stage} · 状态:{status} · 证据:{freshness}\",\n controlNext: \"下一步\",\n ownerDecisionTitle: \"所有者决定\",\n ownerDecisionAdminOnly: \"只有控制台管理员可以记录决定。\",\n ownerDecisionReady: \"当前证据已绑定;请人工选择后记录不可执行意图。\",\n ownerDecisionRecorded: \"已记录:{decision}(仅意图,不会启用实盘)\",\n ownerDecisionApprove: \"批准受限试运行意图\",\n ownerDecisionPark: \"保持暂停\",\n ownerDecisionRetire: \"退役候选\",\n ownerDecisionConfirm: \"这只会记录不可执行的所有者决定意图,不会下单、改资金或启用实盘。继续吗?\",\n ownerDecisionSubmitting: \"正在记录决定…\",\n ownerDecisionSuccess: \"所有者决定已记录;它不会自动启用实盘。\",\n ownerDecisionFailed: \"无法记录所有者决定\",\n executionEvidenceBoard: \"按需查看策略 × 平台证据覆盖\",\n executionEvidenceLoginNotice: \"登录后读取私有执行证据快照;缺失来源不会被推断成 paper 或 live 状态。\",\n executionEvidenceStaleNotice: \"执行证据已超过允许的新鲜度窗口;它保留为历史记录,不能作为当前运行或 P6 结论。\",\n executionEvidenceUnavailableNotice: \"还没有可用的执行证据来源;当前列表保持 fail-closed 空状态。\",\n executionEvidenceUpstreamNotice: \"执行证据已加载,但有 {count} 个上游提示;页面不会补造平台能力或运行资格。\",\n executionEvidenceEmpty: \"暂无可展示的策略 × 平台执行证据。\",\n executionEvidenceMeta: \"{platform} · 当前通道:{environment} · 来源:{source}\",\n executionEvidenceDetail: \"策略:{strategy} · 数据:{data} · 执行:{execution} · Shadow:{shadow} · Paper:{paper}\",\n executionEvidenceNoOrder: \"固定边界:只读证据;不包含账户、订单、资金或 P6 实盘授权。\",\n executionEvidenceNext: \"下一步\",\n adaptiveSelectionBoard: \"按需查看 M1 Shadow 建议\",\n adaptiveSelectionLoginNotice: \"登录后读取私有 Shadow 建议;缺失来源不会被补成策略或订单。\",\n adaptiveSelectionStaleNotice: \"此 Shadow 建议已超出新鲜度窗口,仅保留为历史上下文。\",\n adaptiveSelectionUnavailableNotice: \"还没有可用的 M1 Shadow 建议;页面保持 fail-closed 空状态。\",\n adaptiveSelectionUpstreamNotice: \"Shadow 建议带有 {count} 个上游提示;页面不会推断策略、平台或订单。\",\n adaptiveSelectionEmpty: \"暂无可展示的 Shadow 建议。\",\n adaptiveSelectionMeta: \"{source} · {domain} · 市场截至 {asOf}\",\n adaptiveSelectionRecommended: \"建议观察\",\n adaptiveSelectionNoCandidate: \"无合格 Shadow 候选\",\n adaptiveSelectionReason: \"原因:{reasons}\",\n adaptiveSelectionNoOrder: \"固定边界:仅供人工查看,零建议权重,不改策略、平台、资金、运行状态或订单。\",\n researchTaskBoard: \"按需查看研究任务队列\",\n researchTaskLoginNotice: \"登录后读取私有研究任务索引;没有来源快照时不会展示虚构任务。\",\n researchTaskStaleNotice: \"研究任务来源已超过允许的新鲜度窗口;任务保留为历史记录,但不作为当前工作指令。\",\n researchTaskUnavailableNotice: \"还没有可用的研究任务来源;当前队列保持 fail-closed 空状态。\",\n researchTaskUpstreamNotice: \"研究任务索引已加载,但有 {count} 个上游提示;页面不会补造任务。\",\n researchTaskEmpty: \"暂无可展示的已验证研究任务。\",\n researchTaskMeta: \"{type} · {domain} · 创建于:{created}\",\n researchTaskLimits: \"离线研究:最多 {runs} 次 / {seconds} 秒 · P1 {p1} · P2 {p2} · P3 {p3}\",\n researchTaskNoOrder: \"固定边界:仅研究、零仓位、无订单;不进入 P4/P5/P6。\",\n healthEyebrow: \"运行保障 / 只读\",\n healthTitle: \"先看机器结论,再决定是否介入。\",\n healthSubtitle: \"健康不是实盘许可;它只告诉你是否要复核、降级或保持观察。\",\n runtimeAuthorityTitle: \"P0–P6 控制面授权状态\",\n runtimeAuthorityNotice: \"这里的 live/default_execution_mode/live_configured 是历史配置元数据,不是当前运行、订单、资金或阶段升级授权。\",\n runtimeAuthorityAsOf: \"截至:{asOf} · P1–P3 non-live 数据获取仍需独立、精确的契约;P4–P6 尚未定义。\",\n healthTotal: \"策略总数\",\n healthHealthy: \"健康\",\n healthWatch: \"观察\",\n healthReview: \"需要复核\",\n healthCritical: \"严重\",\n healthBoard: \"策略状态\",\n bootKicker: \"初始化控制台\",\n bootTitle: \"读取策略配置\",\n bootMessage: \"正在读取登录状态、账号配置和当前状态。\",\n bootStrategy: \"正在读取策略目录。\",\n bootSession: \"正在验证登录状态。\",\n bootConfig: \"正在读取账号配置和当前状态。\",\n bootTimeout: \"加载超时,已切换到公开预览(登录后可重试)。\",\n bootPublic: \"公开预览已就绪。\",\n login: \"登录\",\n logout: \"退出\",\n signedInAs: \"已登录 {login}\",\n planEyebrow: \"运行计划 / 受限变更\",\n planTitle: \"先定义边界,再提交配置。\",\n planSubtitle: \"一次只为一个平台和账号准备一个可审计的运行计划。页面只提交配置意图;运行、订单和实盘授权由独立的证据与阶段门槛决定。\",\n planScopeTitle: \"选择范围\",\n planScopeSubtitle: \"平台、账号、策略与目标环境\",\n planRuntimeTitle: \"运行保护\",\n planRuntimeSubtitle: \"停用、插件和附加层都受同一计划约束\",\n planOverlayTitle: \"策略附加层\",\n planOverlaySubtitle: \"仅使用策略已定义的默认边界\",\n planCashSubtitle: \"现金预留优先于融资;两者不能同时覆盖\",\n activePlatform: \"目标平台\",\n account: \"目标账号\",\n strategy: \"策略\",\n mode: \"运行环境\",\n live: \"实盘(需独立授权)\",\n paper: \"旧版非实盘\",\n dryRun: \"演练(不下单)\",\n liveModeUnavailable: \"当前没有同时满足策略发布、运行许可与证据门槛的实盘候选;请保持非实盘。\",\n runtimeTargetMode: \"账号运行状态\",\n runtimeSectionTitle: \"运行与插件\",\n runtimeTargetCurrent: \"沿用当前状态\",\n runtimeTargetEnabled: \"启用\",\n runtimeTargetDisabled: \"禁用\",\n runtimeTargetModeMeta: \"停用后正式运行会跳过,模拟运行和健康检查仍可用。\",\n pluginMode: \"插件状态\",\n pluginModeNone: \"不挂载旧插件\",\n pluginModeMeta: \"当前候选未绑定插件;旧插件不会自动挂载。\",\n incomeLayerMode: \"收入层状态\",\n incomeLayerSectionTitle: \"收入层\",\n incomeLayerCurrent: \"沿用当前配置\",\n incomeLayerEnabled: \"开启收入层\",\n incomeLayerDisabled: \"关闭收入层\",\n incomeLayerNotSupported: \"该策略未定义收入层\",\n incomeLayerStartUsd: \"收入层起始金额\",\n incomeLayerMaxRatio: \"收入层最高比例\",\n incomeLayerModeMeta: \"仅对已定义收入层的美股策略生效。\",\n incomeLayerDefaultMeta: \"策略默认:起始 {start},最高 {ratio}。\",\n incomeLayerAllocationMeta: \"默认分配:{allocations}。\",\n incomeLayerStartMeta: \"总资产达到该金额后启用收入层。\",\n incomeLayerRatioMeta: \"例如 0.55 表示最高 55%。\",\n optionOverlayMode: \"期权层状态\",\n optionOverlaySectionTitle: \"期权层\",\n optionOverlayCurrent: \"沿用当前配置\",\n optionOverlayEnabled: \"启用期权层\",\n optionOverlayDisabled: \"关闭期权层\",\n optionOverlayNotSupported: \"该策略未定义期权层\",\n optionOverlayModeMeta: \"启用时使用策略默认的最佳 recipe 和预算,不在这里手动调比例。\",\n optionOverlayDefaultMeta: \"{defaults}\",\n optionOverlayFamilyGrowth: \"增长\",\n optionOverlayFamilyIncome: \"收入\",\n optionOverlayBudgetRatio: \"预算 {ratio}\",\n optionOverlayRiskRatio: \"风险 {ratio}\",\n cashOnlyExecutionMode: \"允许融资\",\n cashOnlyExecutionCurrent: \"沿用当前配置\",\n cashOnlyExecutionYes: \"是\",\n cashOnlyExecutionNo: \"否\",\n cashOnlyExecutionModeMeta: \"选「否」时只按真实现金下单,不会动用 margin 购买力。\",\n cashOnlyExecutionValueYes: \"是\",\n cashOnlyExecutionValueNo: \"否\",\n currentCashOnlyExecution: \"当前允许融资\",\n pendingCashOnlyExecution: \"待提交允许融资\",\n executionCashPolicyTitle: \"资本边界\",\n executionCashPolicyNote: \"允许融资与预留现金覆盖不能同时生效;选「是」会清空预留覆盖,设预留覆盖会强制「否」。\",\n executionCashMarginBlocksReserve: \"已选允许融资;提交时会清空预留现金覆盖。\",\n executionCashReserveBlocksMargin: \"已设预留现金覆盖;提交时会强制不允许融资。\",\n qmtPlatformCashNote: \"A 股 QMT 不使用 margin / 平台预留现金;现金约束在策略参数 execution_cash_reserve_ratio 内配置。\",\n qmtDryRunOnlyNote: \"当前平台仅支持不下单演练,尚无可用的实盘执行通道。\",\n binancePlatformNote: \"Binance 平台不使用券商级收入层与期权层;相关功能由策略内部实现。\",\n invalidExecutionCashPolicyNote: \"允许融资与预留现金覆盖冲突,请只保留一种约束。\",\n dcaMode: \"定投模式\",\n dcaSectionTitle: \"定投\",\n dcaModeFixed: \"定额定投\",\n dcaModeSmart: \"智能定投\",\n dcaBaseInvestmentUsd: \"定投基准金额\",\n dcaModeMeta: \"仅定投策略可配置。\",\n dcaDefaultMeta: \"默认:{mode},基准金额 {amount}。\",\n dcaNotSupported: \"该策略不是定投策略\",\n dcaPlatformNotSupported: \"当前平台不支持定投策略\",\n currentDca: \"当前定投设置\",\n pendingDca: \"待提交定投设置\",\n dcaText: \"{mode},基准金额 {amount}\",\n minReservedCash: \"最小预留现金 ({currency})\",\n reservedCashRatio: \"预留现金比例\",\n reservedCashMode: \"预留现金策略\",\n reservePolicyCurrent: \"沿用当前配置\",\n reservePolicyNone: \"不设置平台预留现金\",\n reservePolicyRatio: \"仅按比例\",\n reservePolicyFloor: \"仅按固定金额\",\n reservePolicyMax: \"固定金额和比例取较大值\",\n reservedCashModeMeta: \"选择是否沿用、清空或覆盖平台预留现金。\",\n reservedCashNone: \"不设置\",\n reservedCashDefault: \"未配置(平台默认:0 {currency} / 0%)\",\n reservedCashMeta: \"固定金额下限,可单独设置或与比例取较大值。\",\n reservedCashRatioMeta: \"例如 0.03 表示 3%。\",\n summary: \"风险与变更摘要\",\n summaryCurrent: \"当前边界\",\n summaryPending: \"待提交变更\",\n planReadinessTitle: \"提交前核对\",\n planCheckAccount: \"账号与作用范围\",\n planCheckStrategy: \"策略与目标环境\",\n planCheckRisk: \"现金与风险边界\",\n planCheckAuthority: \"实盘执行授权\",\n planCheckWaiting: \"等待配置\",\n planCheckSelected: \"已读取\",\n planCheckValid: \"已校验\",\n planCheckFix: \"需修正\",\n planCheckNonLive: \"非实盘\",\n planCheckNoAuthority: \"未授予\",\n planAuditNote: \"提交记录会写入审计链;它不等同于订单、资金或实盘许可。\",\n copySummary: \"复制状态\",\n loginToRun: \"登录后提交计划\",\n loadingConfig: \"读取配置中\",\n configureAccounts: \"配置账号后切换\",\n runSwitch: \"提交受限变更\",\n noChanges: \"无变更\",\n readonlyNote: \"登录后才可提交受限配置。\",\n publicReadonly: \"登录后查看账号配置。\",\n loadingConfigNote: \"正在读取账号配置和当前状态。\",\n missingConfigNote: \"账号配置未加载,暂时不能执行。\",\n readyNote: \"提交的是受限配置变更;它不授予订单、资金或实盘权限。\",\n invalidStrategyNote: \"当前账号没有可执行策略,暂时不能切换。\",\n invalidReservePolicyNote: \"请为当前预留现金策略填写有效金额或比例。\",\n invalidIncomeLayerNote: \"请填写有效的收入层起始金额和最高比例。\",\n invalidOptionOverlayNote: \"当前策略未定义可启用的期权层。\",\n invalidDcaNote: \"请填写有效的定投模式和基准金额。\",\n publicOauthTitle: \"GitHub OAuth 保护\",\n publicOauthText: \"只允许白名单账号进入私有配置。\",\n publicWorkerTitle: \"Worker 端触发\",\n publicWorkerText: \"令牌保留在服务端,浏览器只提交切换意图。\",\n publicAuditTitle: \"变更可回溯\",\n publicAuditText: \"受限配置变更由 GitHub Actions 执行,便于审计和回滚。\",\n noAccount: \"没有账号选项\",\n noStrategy: \"没有支持的策略\",\n repository: \"平台仓库\",\n selectedAccount: \"账号\",\n selectedMarket: \"市场\",\n currentRuntimeTarget: \"当前账号状态\",\n pendingRuntimeTarget: \"待提交账号状态\",\n reservedCashPolicy: \"当前预留现金\",\n currentIncomeLayer: \"当前收入层\",\n pendingIncomeLayer: \"待提交收入层\",\n currentOptionOverlay: \"当前期权层\",\n pendingOptionOverlay: \"待提交期权层\",\n pendingReservedCashPolicy: \"待提交预留现金\",\n pendingMode: \"待提交模式\",\n currentPluginMode: \"当前插件范围\",\n pendingPluginMode: \"待提交插件范围\",\n unchanged: \"不变\",\n copied: \"已复制状态\",\n dispatching: \"正在触发工作流...\",\n dispatched: \"已触发工作流\",\n dispatchFailed: \"触发失败\",\n targetMeta: \"目标 {target} · 服务 {service} · 市场 {domains}\",\n strategyMeta: \"支持市场:{domains}\",\n strategyLifecycleMeta: \"当前门槛 {stage}\",\n strategyBlockedCountMeta: \"{count} 个策略未达 live 门槛\",\n strategyDefaultBlockedMeta: \"默认策略 {profile} 已阻断:{reason}\",\n usEquity: \"美股\",\n hkEquity: \"港股\",\n cnEquity: \"A股\",\n cryptoEquity: \"加密\",\n currentStrategy: \"当前策略\",\n nextStrategy: \"切换策略\",\n notRead: \"读取失败\",\n runtimeTargetOn: \"启用\",\n runtimeTargetOff: \"禁用\",\n incomeLayerDefault: \"开启,{start}起 {ratio}\",\n incomeLayerOff: \"关闭\",\n incomeLayerOn: \"开启,起始 {start},最高 {ratio}\",\n optionOverlayOff: \"关闭\",\n optionOverlayOn: \"开启\",\n optionOverlayDefaultSimple: \"开启\",\n optionOverlayDefault: \"开启,{detail}\",\n cashOnlyExecutionDefault: \"仅用现金\",\n },\n en: {\n appTitle: \"QuantStrategyLab\",\n appSubtitle: \"Verified evidence, runtime eligibility, and owner decisions.\",\n controlPlaneView: \"Attention\",\n healthView: \"Runtime guard\",\n switchView: \"Run plan\",\n controlPlaneEyebrow: \"Today / read only\",\n controlPlaneTitle: \"Only decide what needs you.\",\n controlPlaneSubtitle: \"This is not a trading screen: it shows verified follow-ups, evidence coverage, and the boundaries that need an owner decision.\",\n controlCandidateTotal: \"Candidates\",\n controlDeferred: \"Deferred now\",\n controlParked: \"Parked\",\n controlOwnerDecision: \"Needs my decision\",\n controlCandidateBoard: \"Follow-ups\",\n controlDataReady: \"Snapshot synced (not strategy readiness)\",\n controlDataStale: \"Snapshot is stale\",\n controlDataUnavailable: \"Waiting for a usable snapshot\",\n controlAttentionResearchOnly: \"Research only; no execution authority\",\n controlAttentionRequired: \"Research state needs attention\",\n controlAttentionUnavailable: \"No verified research state\",\n controlComputedAt: \"Last computed: {time}\",\n controlLoginNotice: \"Sign in to read the private global snapshot. Missing data is never invented.\",\n controlStaleNotice: \"This snapshot is outside its freshness window. The record remains visible, but is not a current conclusion.\",\n controlUnavailableNotice: \"No usable global snapshot is available yet. This page remains fail-closed and empty.\",\n controlUpstreamNotice: \"The snapshot loaded with {count} upstream notice(s); missing information is never inferred.\",\n controlAttentionNotice: \"Research needs attention ({deferred} deferred, {parked} parked, {signals} signal(s)). Snapshot delivery is not strategy validation or execution authority; the system remains no-order.\",\n controlEmptyCandidates: \"No candidate snapshot is available to display.\",\n controlNoRecommendation: \"No machine recommendation is available.\",\n controlStageMeta: \"Stage: {stage} · status: {status} · evidence: {freshness}\",\n controlNext: \"NEXT\",\n ownerDecisionTitle: \"Owner decision\",\n ownerDecisionAdminOnly: \"Only console administrators can record a decision.\",\n ownerDecisionReady: \"Current evidence is bound; choose an action to record a non-executable intent.\",\n ownerDecisionRecorded: \"Recorded: {decision} (intent only; Live remains disabled)\",\n ownerDecisionApprove: \"Approve limited-canary intent\",\n ownerDecisionPark: \"Keep parked\",\n ownerDecisionRetire: \"Retire candidate\",\n ownerDecisionConfirm: \"This records a non-executable owner intent only. It will not place orders, change funds, or enable Live. Continue?\",\n ownerDecisionSubmitting: \"Recording decision…\",\n ownerDecisionSuccess: \"Owner decision recorded; it does not enable Live automatically.\",\n ownerDecisionFailed: \"Could not record owner decision\",\n executionEvidenceBoard: \"View strategy × platform evidence on demand\",\n executionEvidenceLoginNotice: \"Sign in to read private execution-evidence snapshots. Missing sources never imply a paper or live state.\",\n executionEvidenceStaleNotice: \"This execution evidence is beyond its freshness window. It remains historical context, not a current runtime or P6 conclusion.\",\n executionEvidenceUnavailableNotice: \"No usable execution-evidence source is available yet. This list remains fail-closed and empty.\",\n executionEvidenceUpstreamNotice: \"Execution evidence loaded with {count} upstream notice(s); no platform capability or runtime authority is inferred.\",\n executionEvidenceEmpty: \"No strategy × platform execution evidence is available to display.\",\n executionEvidenceMeta: \"{platform} · current lane: {environment} · source: {source}\",\n executionEvidenceDetail: \"strategy: {strategy} · data: {data} · execution: {execution} · shadow: {shadow} · paper: {paper}\",\n executionEvidenceNoOrder: \"Fixed boundary: read-only evidence; no account, order, funds, or P6 live authority.\",\n executionEvidenceNext: \"NEXT\",\n adaptiveSelectionBoard: \"View M1 Shadow suggestions on demand\",\n adaptiveSelectionLoginNotice: \"Sign in to read private Shadow suggestions. Missing sources never imply a strategy or order.\",\n adaptiveSelectionStaleNotice: \"This Shadow suggestion is beyond its freshness window and remains historical context only.\",\n adaptiveSelectionUnavailableNotice: \"No usable M1 Shadow suggestion is available yet. This view remains fail-closed and empty.\",\n adaptiveSelectionUpstreamNotice: \"Shadow suggestions include {count} upstream notice(s); no strategy, platform, or order is inferred.\",\n adaptiveSelectionEmpty: \"No Shadow suggestion is available to display.\",\n adaptiveSelectionMeta: \"{source} · {domain} · market as of {asOf}\",\n adaptiveSelectionRecommended: \"Observe suggestion\",\n adaptiveSelectionNoCandidate: \"No eligible Shadow candidate\",\n adaptiveSelectionReason: \"Reasons: {reasons}\",\n adaptiveSelectionNoOrder: \"Fixed boundary: human-readable only, zero proposed weight, no strategy, platform, funds, runtime, or order change.\",\n researchTaskBoard: \"View research queue on demand\",\n researchTaskLoginNotice: \"Sign in to read the private research task index. Missing source snapshots never imply a task.\",\n researchTaskStaleNotice: \"The research task source is beyond its freshness window. It remains historical context, not a current instruction.\",\n researchTaskUnavailableNotice: \"No research task source is available yet. The queue remains fail-closed and empty.\",\n researchTaskUpstreamNotice: \"The research task index loaded with {count} upstream notices; no task is inferred from missing data.\",\n researchTaskEmpty: \"No verified research task is available to display.\",\n researchTaskMeta: \"{type} · {domain} · created {created}\",\n researchTaskLimits: \"Offline research: up to {runs} run(s) / {seconds}s · P1 {p1} · P2 {p2} · P3 {p3}\",\n researchTaskNoOrder: \"Fixed boundary: research only, zero size, no order; never P4/P5/P6.\",\n healthEyebrow: \"Runtime guard / read only\",\n healthTitle: \"Read the machine conclusion before deciding to intervene.\",\n healthSubtitle: \"Health is not live approval. It only tells you whether to review, reduce, or keep watching.\",\n runtimeAuthorityTitle: \"P0–P6 control-plane authority\",\n runtimeAuthorityNotice: \"live/default_execution_mode/live_configured are legacy configuration metadata, not authorization for current runtime, orders, funds, or stage changes.\",\n runtimeAuthorityAsOf: \"As of {asOf} · P1–P3 non-live data acquisition still needs its own precise contract; P4–P6 are undefined.\",\n healthTotal: \"Strategies\",\n healthHealthy: \"Healthy\",\n healthWatch: \"Watch\",\n healthReview: \"Review\",\n healthCritical: \"Critical\",\n healthBoard: \"Strategy status\",\n bootKicker: \"Starting console\",\n bootTitle: \"Loading strategy config\",\n bootMessage: \"Reading session, account config, and current state.\",\n bootStrategy: \"Reading strategy catalog.\",\n bootSession: \"Checking sign-in status.\",\n bootConfig: \"Reading account config and current state.\",\n bootTimeout: \"Loading timed out; switched to public preview. Retry after signing in.\",\n bootPublic: \"Public preview is ready.\",\n login: \"Sign in\",\n logout: \"Sign out\",\n signedInAs: \"Signed in as {login}\",\n planEyebrow: \"Run plan / bounded change\",\n planTitle: \"Set the boundary before submitting configuration.\",\n planSubtitle: \"Prepare one auditable plan for one platform and account. This page submits configuration intent only; runtime, order, and live authority remain governed by independent evidence and lifecycle gates.\",\n planScopeTitle: \"Choose scope\",\n planScopeSubtitle: \"Platform, account, strategy, and target environment\",\n planRuntimeTitle: \"Runtime protection\",\n planRuntimeSubtitle: \"Disable state, plugins, and overlays share one plan boundary\",\n planOverlayTitle: \"Strategy overlays\",\n planOverlaySubtitle: \"Only strategy-defined default guardrails are available\",\n planCashSubtitle: \"Cash reserve takes precedence over margin; they cannot override together\",\n activePlatform: \"Target platform\",\n account: \"Target account\",\n strategy: \"Strategy\",\n mode: \"Target environment\",\n live: \"Live (independent approval required)\",\n paper: \"Legacy non-live\",\n dryRun: \"Dry run (no orders)\",\n liveModeUnavailable: \"No candidate currently meets the strategy-release, runtime-eligibility, and evidence gates for Live. Keep this plan non-live.\",\n runtimeTargetMode: \"Account status\",\n runtimeSectionTitle: \"Runtime and plugins\",\n runtimeTargetCurrent: \"Keep current status\",\n runtimeTargetEnabled: \"Enabled\",\n runtimeTargetDisabled: \"Disabled\",\n runtimeTargetModeMeta: \"Disabled accounts skip live runs; dry runs and health checks still work.\",\n pluginMode: \"Plugin status\",\n pluginModeNone: \"Do not mount legacy plugins\",\n pluginModeMeta: \"The current candidate has no bound plugin; legacy plugins are not auto-mounted.\",\n incomeLayerMode: \"Income layer\",\n incomeLayerSectionTitle: \"Income layer\",\n incomeLayerCurrent: \"Keep current config\",\n incomeLayerEnabled: \"Enable income layer\",\n incomeLayerDisabled: \"Disable income layer\",\n incomeLayerNotSupported: \"No income layer for this strategy\",\n incomeLayerStartUsd: \"Income layer start amount\",\n incomeLayerMaxRatio: \"Income layer max ratio\",\n incomeLayerModeMeta: \"Only applies to US equity strategies with an income layer.\",\n incomeLayerDefaultMeta: \"Strategy default: starts at {start}, max {ratio}.\",\n incomeLayerAllocationMeta: \"Default allocation: {allocations}.\",\n incomeLayerStartMeta: \"Income layer activates after total assets reach this amount.\",\n incomeLayerRatioMeta: \"Use 0.55 for a 55% cap.\",\n optionOverlayMode: \"Option layer\",\n optionOverlaySectionTitle: \"Option layer\",\n optionOverlayCurrent: \"Keep current config\",\n optionOverlayEnabled: \"Enable option layer\",\n optionOverlayDisabled: \"Disable option layer\",\n optionOverlayNotSupported: \"No option layer for this strategy\",\n optionOverlayModeMeta: \"Enabled mode uses the strategy's default recipe and budget; ratios are not edited here.\",\n optionOverlayDefaultMeta: \"{defaults}\",\n optionOverlayFamilyGrowth: \"Growth\",\n optionOverlayFamilyIncome: \"Income\",\n optionOverlayBudgetRatio: \"budget {ratio}\",\n optionOverlayRiskRatio: \"risk {ratio}\",\n cashOnlyExecutionMode: \"Allow margin\",\n cashOnlyExecutionCurrent: \"Keep current config\",\n cashOnlyExecutionYes: \"Yes\",\n cashOnlyExecutionNo: \"No\",\n cashOnlyExecutionModeMeta: \"Choose No to use available cash only and avoid margin buying power.\",\n cashOnlyExecutionValueYes: \"Yes\",\n cashOnlyExecutionValueNo: \"No\",\n currentCashOnlyExecution: \"Current allow margin\",\n pendingCashOnlyExecution: \"Pending allow margin\",\n executionCashPolicyTitle: \"Capital boundary\",\n executionCashPolicyNote: \"Allow margin and reserve-cash overrides cannot both apply. Yes clears reserve overrides; reserve overrides force No.\",\n executionCashMarginBlocksReserve: \"Allow margin is selected; submitting will clear reserve-cash overrides.\",\n executionCashReserveBlocksMargin: \"Reserve-cash override is active; submitting will force allow margin to No.\",\n qmtPlatformCashNote: \"QMT A-share does not use margin or platform reserve cash; cash constraints live in strategy execution_cash_reserve_ratio.\",\n qmtDryRunOnlyNote: \"This platform supports no-order dry runs only; no live execution route is configured.\",\n binancePlatformNote: \"Binance does not use broker-level income/option layers; features are implemented inside strategies.\",\n invalidExecutionCashPolicyNote: \"Allow margin and reserve-cash overrides conflict. Keep only one constraint.\",\n dcaMode: \"DCA mode\",\n dcaSectionTitle: \"DCA\",\n dcaModeFixed: \"Fixed DCA\",\n dcaModeSmart: \"Smart DCA\",\n dcaBaseInvestmentUsd: \"Base DCA amount\",\n dcaModeMeta: \"Only DCA strategies can use this.\",\n dcaDefaultMeta: \"Default: {mode}, base amount {amount}.\",\n dcaNotSupported: \"This is not a DCA strategy\",\n dcaPlatformNotSupported: \"DCA not supported on this platform\",\n currentDca: \"Current DCA settings\",\n pendingDca: \"Pending DCA settings\",\n dcaText: \"{mode}, base amount {amount}\",\n minReservedCash: \"Minimum reserved cash ({currency})\",\n reservedCashRatio: \"Reserved cash ratio\",\n reservedCashMode: \"Reserved cash policy\",\n reservePolicyCurrent: \"Keep current config\",\n reservePolicyNone: \"No platform reserve\",\n reservePolicyRatio: \"Ratio only\",\n reservePolicyFloor: \"Fixed amount only\",\n reservePolicyMax: \"Max of amount and ratio\",\n reservedCashModeMeta: \"Choose whether to keep, clear, or override platform reserved cash.\",\n reservedCashNone: \"None\",\n reservedCashDefault: \"Not configured (platform default: 0 {currency} / 0%)\",\n reservedCashMeta: \"Fixed cash floor. Use alone or with a ratio.\",\n reservedCashRatioMeta: \"Use 0.03 for 3%.\",\n summary: \"Risk and change summary\",\n summaryCurrent: \"Current boundary\",\n summaryPending: \"Pending change\",\n planReadinessTitle: \"Before submission\",\n planCheckAccount: \"Account and scope\",\n planCheckStrategy: \"Strategy and environment\",\n planCheckRisk: \"Cash and risk boundary\",\n planCheckAuthority: \"Live execution authority\",\n planCheckWaiting: \"Waiting\",\n planCheckSelected: \"Read\",\n planCheckValid: \"Checked\",\n planCheckFix: \"Needs correction\",\n planCheckNonLive: \"Non-live\",\n planCheckNoAuthority: \"Not granted\",\n planAuditNote: \"A submission creates an audit record; it is not an order, funding, or live permission.\",\n copySummary: \"Copy state\",\n loginToRun: \"Sign in to submit a plan\",\n loadingConfig: \"Loading config\",\n configureAccounts: \"Configure accounts\",\n runSwitch: \"Submit bounded change\",\n noChanges: \"No changes\",\n readonlyNote: \"Sign in to submit a bounded configuration.\",\n publicReadonly: \"Sign in to view account config.\",\n loadingConfigNote: \"Reading account config and current state.\",\n missingConfigNote: \"Account config is not loaded, so switching is disabled.\",\n readyNote: \"This submits a bounded configuration change; it grants no order, funding, or live authority.\",\n invalidStrategyNote: \"This account has no runnable strategy, so switching is disabled.\",\n invalidReservePolicyNote: \"Enter a valid amount or ratio for the selected reserved-cash policy.\",\n invalidIncomeLayerNote: \"Enter a valid income layer start amount and max ratio.\",\n invalidOptionOverlayNote: \"This strategy does not define an option layer to enable.\",\n invalidDcaNote: \"Enter a valid DCA mode and base amount.\",\n publicOauthTitle: \"Protected by GitHub OAuth\",\n publicOauthText: \"Only allowlisted accounts can open private config.\",\n publicWorkerTitle: \"Worker-side dispatch\",\n publicWorkerText: \"Tokens stay server-side; the browser submits intent only.\",\n publicAuditTitle: \"Traceable changes\",\n publicAuditText: \"Bounded configuration changes run through GitHub Actions for audit and rollback.\",\n noAccount: \"No accounts\",\n noStrategy: \"No supported strategies\",\n repository: \"Repository\",\n selectedAccount: \"Account\",\n selectedMarket: \"Market\",\n currentRuntimeTarget: \"Current account status\",\n pendingRuntimeTarget: \"Pending account status\",\n reservedCashPolicy: \"Current reserved cash\",\n currentIncomeLayer: \"Current income layer\",\n pendingIncomeLayer: \"Pending income layer\",\n currentOptionOverlay: \"Current option layer\",\n pendingOptionOverlay: \"Pending option layer\",\n pendingReservedCashPolicy: \"Pending reserved cash\",\n pendingMode: \"Pending mode\",\n currentPluginMode: \"Current plugin scope\",\n pendingPluginMode: \"Pending plugin scope\",\n unchanged: \"Unchanged\",\n copied: \"State copied\",\n dispatching: \"Dispatching workflow...\",\n dispatched: \"Workflow dispatched\",\n dispatchFailed: \"Dispatch failed\",\n targetMeta: \"target {target} · service {service} · market {domains}\",\n strategyMeta: \"Markets: {domains}\",\n strategyLifecycleMeta: \"current gate {stage}\",\n strategyBlockedCountMeta: \"{count} strategies are blocked from live\",\n strategyDefaultBlockedMeta: \"Default strategy {profile} is blocked: {reason}\",\n usEquity: \"US equity\",\n hkEquity: \"HK equity\",\n cnEquity: \"CN A-share\",\n cryptoEquity: \"Crypto\",\n currentStrategy: \"Current strategy\",\n nextStrategy: \"Switch strategy\",\n notRead: \"Not read\",\n runtimeTargetOn: \"Enabled\",\n runtimeTargetOff: \"Disabled\",\n incomeLayerDefault: \"Enabled, {start} start, {ratio} max\",\n incomeLayerOff: \"Disabled\",\n incomeLayerOn: \"Enabled, starts at {start}, max {ratio}\",\n optionOverlayOff: \"Disabled\",\n optionOverlayOn: \"Enabled\",\n optionOverlayDefaultSimple: \"Strategy default: enabled\",\n optionOverlayDefault: \"Enabled, {detail}\",\n cashOnlyExecutionDefault: \"Cash only\",\n },\n };\n\n const storedLang = localStorage.getItem(\"qsl-switch-lang\");\n const initialLang = storedLang === \"zh\" || storedLang === \"en\"\n ? storedLang\n : ((navigator.language || \"\").toLowerCase().startsWith(\"zh\") ? \"zh\" : \"en\");\n const clone = (value) => JSON.parse(JSON.stringify(value));\n const defaultReserveForm = () => ({\n reservePolicyMode: \"current\",\n minReservedCashUsd: \"\",\n reservedCashRatio: \"\",\n reservedCashTouched: false,\n incomeLayerMode: \"current\",\n incomeLayerStartUsd: \"\",\n incomeLayerMaxRatio: \"\",\n incomeLayerTouched: false,\n optionOverlayMode: \"current\",\n optionOverlayTouched: false,\n cashOnlyExecutionMode: \"current\",\n cashOnlyExecutionTouched: false,\n runtimeTargetMode: \"current\",\n runtimeTargetTouched: false,\n dcaMode: \"fixed\",\n dcaBaseInvestmentUsd: \"\",\n dcaTouched: false,\n strategyTouched: false,\n });\n\n const state = {\n selected: \"longbridge\",\n lang: initialLang,\n view: \"control\",\n appReady: false,\n bootMessageKey: \"bootMessage\",\n auth: { available: false, allowed: false, admin: false, login: null },\n accountOptions: clone(defaultAccountOptions),\n currentStrategies: {},\n health: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { strategy_count: 0, healthy: 0, watch: 0, review: 0, critical: 0 },\n strategies: [],\n },\n filter: \"all\",\n },\n controlPlane: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { candidate_count: 0, deferred: 0, parked: 0, owner_decision_required: 0 },\n attention: { status: \"unavailable\", reason_codes: [\"control_plane_source_unavailable\"] },\n candidates: [],\n policy: { p4_p5_automation: \"not_configured\", p6_owner_decision_required: true },\n errors: [],\n },\n },\n ownerDecisions: {\n data_status: \"unavailable\",\n candidates: [],\n errors: [],\n },\n adaptiveSelection: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { source_count: 0, decision_count: 0, candidate_count: 0, recommended_count: 0, rejected_candidate_count: 0 },\n selections: [],\n policy: { authority: \"shadow_only\", no_order: true, execution_authority_granted: false },\n errors: [],\n },\n },\n executionEvidence: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { deployment_count: 0, autonomous_shadow: 0, autonomous_paper: 0, owner_canary_decision: 0, parked: 0 },\n deployments: [],\n policy: { execution_evidence_read_only: true, p6_owner_decision_required: true, limited_live_canary_active: false },\n errors: [],\n },\n },\n researchTasks: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { task_count: 0 },\n tasks: [],\n policy: { research_only: true, no_order: true, size_zero_required: true, p4_p5_p6_authorized: false },\n errors: [],\n },\n },\n configSource: \"default\",\n repositories: clone(defaultRepositories),\n forms: {\n longbridge: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n ibkr: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n schwab: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n firstrade: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n qmt: { accountKey: \"preview\", strategy: \"\", executionMode: \"paper\", pluginMode: \"none\", ...defaultReserveForm() },\n binance: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\" },\n },\n };\n\n const el = (id) => document.getElementById(id);\n const t = (key) => copy[state.lang][key] || copy.en[key] || key;\n let toastTimer = null;\n\n function showToast(message, { duration = 4000 } = {}) {\n const node = el(\"toast\");\n if (toastTimer) {\n window.clearTimeout(toastTimer);\n toastTimer = null;\n }\n node.textContent = message || \"\";\n if (message && duration > 0) {\n toastTimer = window.setTimeout(() => {\n node.textContent = \"\";\n toastTimer = null;\n }, duration);\n }\n }\n\n async function fetchWithTimeout(url, init = {}, timeoutMs = APP_BOOT_TIMEOUT_MS) {\n const controller = new AbortController();\n const timeoutId = window.setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (error) {\n if (error?.name === \"AbortError\") {\n throw new Error(\"request timeout\");\n }\n throw error;\n } finally {\n window.clearTimeout(timeoutId);\n }\n }\n\n async function requestJson(url, init = {}, timeoutMs = APP_BOOT_TIMEOUT_MS) {\n const response = await fetchWithTimeout(url, { ...init, cache: \"no-store\" }, timeoutMs);\n if (!response.ok) throw new Error(\"request failed\");\n return response.json();\n }\n\n function isRequestTimeoutError(error) {\n return String(error?.message || \"\").toLowerCase() === \"request timeout\";\n }\n\n function optionsFor(platform) {\n return state.accountOptions[platform] && state.accountOptions[platform].length\n ? state.accountOptions[platform]\n : defaultAccountOptions[platform];\n }\n\n function selectedAccount(platform = state.selected) {\n const options = optionsFor(platform);\n const form = state.forms[platform];\n return options.find((option) => option.key === form.accountKey) || options[0];\n }\n\n function hasPrivateConfig() {\n return Boolean(state.auth.allowed && state.configSource === \"private\");\n }\n\n function cleanStrategyProfile(value) {\n const profile = String(value || \"\").trim();\n return /^[a-z0-9._=-]{1,120}$/.test(profile) ? profile : \"\";\n }\n\n function cleanStrategyDomain(value) {\n const domain = String(value || \"\").trim();\n return strategyDomains.includes(domain) ? domain : \"\";\n }\n\n function domainLabel(domain) {\n const entry = domainLabels[domain];\n if (entry) return state.lang === \"zh\" ? entry.zh : entry.en;\n return domain;\n }\n\n function platformSupportsMarginPolicy(platform = state.selected) {\n return platformConfig[platform]?.margin_policy ?? true;\n }\n\n function platformSupportsReservedCashPolicy(platform = state.selected) {\n return platformConfig[platform]?.reserved_cash ?? true;\n }\n\n function platformDryRunOnly(platform = state.selected) {\n return platformConfig[platform]?.dry_run_only ?? false;\n }\n\n function supportedExecutionModesForPlatform(platform = state.selected) {\n const modes = platformConfig[platform]?.supported_execution_modes;\n return Array.isArray(modes) ? modes.filter((mode) => mode === \"live\" || mode === \"dry_run\") : [];\n }\n\n function allowMarginExplicitlySelected(form) {\n return normalizeCashOnlyExecutionMode(form?.cashOnlyExecutionMode) === \"disabled\";\n }\n\n function reserveCashOverrideActive(form) {\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n return mode === \"ratio\" || mode === \"floor\" || mode === \"max\";\n }\n\n function executionCashPolicyConflict(form) {\n return allowMarginExplicitlySelected(form) && reserveCashOverrideActive(form);\n }\n\n function reconcileExecutionCashPolicy(form, changed) {\n if (!form) return;\n if (changed === \"margin\" && allowMarginExplicitlySelected(form)) {\n if (form.reservePolicyMode !== \"none\" && (form.minReservedCashUsd || form.reservedCashRatio)) {\n form._prevReserve = {\n mode: form.reservePolicyMode,\n floor: form.minReservedCashUsd,\n ratio: form.reservedCashRatio,\n };\n }\n form.reservePolicyMode = \"none\";\n form.reservedCashTouched = true;\n } else if (changed === \"reserve\" && reserveCashOverrideActive(form)) {\n form.cashOnlyExecutionMode = \"enabled\";\n form.cashOnlyExecutionTouched = true;\n }\n }\n\n function restoreReserveAfterMarginDisabled(form) {\n if (!form || allowMarginExplicitlySelected(form) || !form._prevReserve) return;\n form.reservePolicyMode = form._prevReserve.mode;\n form.minReservedCashUsd = form._prevReserve.floor;\n form.reservedCashRatio = form._prevReserve.ratio;\n form.reservedCashTouched = true;\n delete form._prevReserve;\n }\n\n function strategyDomain(profile) {\n return strategyCatalog[profile]?.domain || \"\";\n }\n\n function selectedCashCurrency(platform = state.selected, account = selectedAccount(platform)) {\n const configured = String(account?.cash_currency || \"\").trim().toUpperCase();\n if (configured === \"USD\" || configured === \"HKD\" || configured === \"CNY\") return configured;\n const domain = strategyDomain(state.forms[platform]?.strategy);\n if (domain === \"hk_equity\") return \"HKD\";\n if (domain === \"cn_equity\") return \"CNY\";\n return \"USD\";\n }\n\n function applyStrategyProfiles(rawProfiles) {\n const profiles = Array.isArray(rawProfiles) && rawProfiles.length\n ? rawProfiles\n : defaultStrategyProfiles;\n const nextOptions = [];\n const nextLabels = {};\n const nextCatalog = {};\n const nextIncomeLayerDefaults = {};\n const nextOptionOverlayDefaults = {};\n for (const item of profiles) {\n const profile = cleanStrategyProfile(item?.profile || item?.strategy_profile);\n if (!profile || nextOptions.includes(profile)) continue;\n const domain = cleanStrategyDomain(item?.domain || \"us_equity\");\n if (!domain) continue;\n nextOptions.push(profile);\n nextLabels[profile] = strategyLabelSet(profile, item);\n nextCatalog[profile] = {\n profile,\n label: nextLabels[profile].en || nextLabels[profile].zh || profile,\n label_en: nextLabels[profile].en || \"\",\n label_zh: nextLabels[profile].zh || \"\",\n domain,\n runtime_enabled: cleanOptionalBoolean(item?.runtime_enabled ?? false) === true,\n };\n const lifecycleStage = normalizeLifecycleStage(item?.lifecycle_stage ?? item?.lifecycleStage);\n if (lifecycleStage) nextCatalog[profile].lifecycle_stage = lifecycleStage;\n const allowedExecutionModes = normalizeAllowedExecutionModes(item?.allowed_execution_modes);\n if (allowedExecutionModes.length) nextCatalog[profile].allowed_execution_modes = allowedExecutionModes;\n const canSwitchLive = cleanOptionalBoolean(item?.can_switch_live);\n if (canSwitchLive !== null) nextCatalog[profile].can_switch_live = canSwitchLive;\n const blockedLiveReason = cleanDisplayText(item?.blocked_live_reason);\n if (blockedLiveReason) nextCatalog[profile].blocked_live_reason = blockedLiveReason;\n const latestEvidenceStatus = cleanDisplayText(item?.latest_evidence_status);\n if (latestEvidenceStatus) nextCatalog[profile].latest_evidence_status = latestEvidenceStatus;\n const pluginGateStatus = cleanDisplayText(item?.plugin_gate_status);\n if (pluginGateStatus) nextCatalog[profile].plugin_gate_status = pluginGateStatus;\n const dcaDefaults = dcaProfileDefaults[profile] || null;\n if (dcaDefaults || item?.dca_enabled === true) {\n nextCatalog[profile].dca_enabled = true;\n nextCatalog[profile].dca_default_mode = normalizeDcaMode(\n item?.dca_default_mode || item?.default_dca_mode || dcaDefaults?.defaultMode || \"fixed\",\n );\n nextCatalog[profile].dca_default_base_investment_usd = cleanDisplayPositiveNumber(\n item?.dca_default_base_investment_usd ||\n item?.default_dca_base_investment_usd ||\n dcaDefaults?.defaultBaseInvestmentUsd ||\n \"1000\",\n ) || \"1000\";\n }\n const profileIncomeDefaults = incomeLayerDefaultsFromProfileItem(item);\n const incomeDefaults = profileIncomeDefaults === false\n ? null\n : (profileIncomeDefaults || fallbackIncomeLayerDefaults[profile] || null);\n if (incomeDefaults) {\n nextIncomeLayerDefaults[profile] = incomeDefaults;\n nextCatalog[profile].income_layer_enabled = true;\n nextCatalog[profile].income_layer_start_usd = String(incomeDefaults.startUsd);\n nextCatalog[profile].income_layer_max_ratio = incomeDefaults.maxRatio;\n nextCatalog[profile].income_layer_allocations = incomeDefaults.allocations;\n }\n const profileOptionDefaults = optionOverlayDefaultsFromProfileItem(item);\n const optionDefaults = profileOptionDefaults === false\n ? null\n : (profileOptionDefaults || fallbackOptionOverlayDefaults[profile] || null);\n if (optionDefaults) {\n nextOptionOverlayDefaults[profile] = optionDefaults;\n nextCatalog[profile].option_overlay_enabled = true;\n nextCatalog[profile].option_overlay_live_gate = optionDefaults.liveGate || \"\";\n nextCatalog[profile].option_overlay_live_status = optionDefaults.liveStatus || \"\";\n }\n }\n if (!nextOptions.length && profiles !== defaultStrategyProfiles) return applyStrategyProfiles(defaultStrategyProfiles);\n strategyOptions = nextOptions;\n strategyLabels = nextLabels;\n strategyCatalog = nextCatalog;\n incomeLayerDefaults = nextIncomeLayerDefaults;\n optionOverlayDefaults = nextOptionOverlayDefaults;\n }\n\n function incomeLayerDefaultsFromProfileItem(item) {\n const enabled = cleanOptionalBoolean(item?.income_layer_enabled);\n const hasConfig = enabled !== null ||\n item?.income_layer_start_usd !== undefined ||\n item?.income_layer_max_ratio !== undefined ||\n item?.income_layer_allocations !== undefined;\n if (!hasConfig) return null;\n if (enabled === false) return false;\n const startUsd = cleanDisplayNumber(item?.income_layer_start_usd);\n const maxRatio = cleanDisplayRatio(item?.income_layer_max_ratio);\n const allocations = cleanIncomeLayerAllocations(item?.income_layer_allocations);\n if (!startUsd || !maxRatio || !allocations) return null;\n return { startUsd, maxRatio, allocations };\n }\n\n function cleanIncomeLayerAllocations(value) {\n if (!value || Array.isArray(value) || typeof value !== \"object\") return null;\n const allocations = {};\n let total = 0;\n for (const [rawSymbol, rawRatio] of Object.entries(value)) {\n const symbol = String(rawSymbol || \"\").trim().toUpperCase();\n const ratio = cleanDisplayPositiveNumber(rawRatio);\n if (!/^[A-Z0-9.-]{1,12}$/.test(symbol) || !ratio) continue;\n allocations[symbol] = Number(ratio);\n total += Number(ratio);\n }\n return total > 0 && Object.keys(allocations).length ? allocations : null;\n }\n\n function optionOverlayDefaultsFromProfileItem(item) {\n const enabled = cleanOptionalBoolean(item?.option_overlay_enabled);\n const hasConfig = enabled !== null ||\n item?.option_growth_overlay_enabled !== undefined ||\n item?.option_income_overlay_enabled !== undefined ||\n item?.option_overlay_live_gate !== undefined ||\n item?.option_overlay_live_status !== undefined;\n if (!hasConfig) return null;\n if (enabled === false) return false;\n const families = [\n optionOverlayFamilyDefaultsFromProfileItem(item, \"growth\"),\n optionOverlayFamilyDefaultsFromProfileItem(item, \"income\"),\n ].filter(Boolean);\n if (!families.length) return null;\n return {\n liveGate: String(item?.option_overlay_live_gate || \"promotion_required\"),\n liveStatus: String(item?.option_overlay_live_status || \"research_only\"),\n families,\n };\n }\n\n function optionOverlayFamilyDefaultsFromProfileItem(item, family) {\n const prefix = `option_${family}_overlay`;\n const enabled = cleanOptionalBoolean(item?.[`${prefix}_enabled`]);\n if (enabled !== true) return null;\n const recipe = cleanStrategyProfile(item?.[`${prefix}_recipe`]);\n const startUsd = cleanDisplayNumber(item?.[`${prefix}_start_usd`]);\n const ratioField = family === \"growth\"\n ? \"option_growth_overlay_nav_budget_ratio\"\n : \"option_income_overlay_nav_risk_ratio\";\n const ratio = cleanDisplayRatio(item?.[ratioField]);\n if (!recipe || !startUsd || !ratio) return null;\n return {\n family,\n recipe,\n startUsd,\n ratio,\n ratioKind: family === \"growth\" ? \"budget\" : \"risk\",\n };\n }\n\n function supportedDomainsForAccount(platform, account) {\n if (Array.isArray(account?.supported_domains) && account.supported_domains.length) {\n const cleaned = account.supported_domains.map(cleanStrategyDomain).filter(Boolean);\n if (cleaned.length) return [...new Set(cleaned)];\n }\n return inferSupportedDomains(platform, account);\n }\n\n function inferSupportedDomains(platform, account) {\n void account;\n if (platform === \"qmt\") return [\"cn_equity\"];\n if (platform === \"longbridge\" || platform === \"ibkr\") return [\"us_equity\", \"hk_equity\"];\n return [\"us_equity\"];\n }\n\n function supportedDomainLabel(platform, account) {\n return supportedDomainsForAccount(platform, account).map(domainLabel).join(\" / \");\n }\n\n function platformSupportsDca(platform = state.selected) {\n return platformConfig[platform]?.dca ?? false;\n }\n\n function strategyCatalogEntry(profile) {\n return strategyCatalog[cleanStrategyProfile(profile)] || {};\n }\n\n function normalizeLifecycleStage(value) {\n const text = String(value || \"\").trim().toLowerCase();\n if (!text || text.length > 40 || !/^[a-z0-9._-]+$/.test(text)) return \"\";\n if ([\"research_backtest_only\", \"ai_monitored_candidate\"].includes(text)) return \"research_active\";\n if (text === \"shadow_candidate\") return \"shadow_active\";\n if (text === \"runtime_enabled\") return \"live_candidate\";\n return text;\n }\n\n function normalizeAllowedExecutionModes(value) {\n const items = Array.isArray(value)\n ? value\n : String(value || \"\").split(/[,\\s/|]+/);\n const modes = [];\n for (const item of items) {\n const mode = String(item || \"\").trim().toLowerCase();\n if (!mode || mode.length > 32 || !/^[a-z0-9._-]+$/.test(mode)) continue;\n if (!modes.includes(mode)) modes.push(mode);\n }\n return modes;\n }\n\n function cleanDisplayText(value) {\n const text = String(value ?? \"\").trim();\n if (!text || text.length > 120 || /[<>]/.test(text)) return \"\";\n return text;\n }\n\n function strategyCanSwitchLive(entry) {\n if (!entry || typeof entry !== \"object\") return false;\n if (entry.runtime_enabled !== true) return false;\n const allowedModes = normalizeAllowedExecutionModes(entry.allowed_execution_modes);\n if (!allowedModes.includes(\"live\")) return false;\n if (cleanOptionalBoolean(entry.can_switch_live) !== true) return false;\n const lifecycleStage = normalizeLifecycleStage(entry.lifecycle_stage);\n if (![\"live_enabled\", \"runtime_enabled\"].includes(lifecycleStage)) return false;\n const blockedReason = cleanDisplayText(entry.blocked_live_reason);\n if (blockedReason) return false;\n const evidenceStatus = cleanDisplayText(entry.latest_evidence_status);\n if (evidenceStatus && [\"research_only\", \"blocked\", \"pending\"].includes(evidenceStatus.toLowerCase())) return false;\n const pluginGateStatus = cleanDisplayText(entry.plugin_gate_status);\n if (pluginGateStatus && [\"blocked\", \"locked\", \"disabled\"].includes(pluginGateStatus.toLowerCase())) return false;\n return true;\n }\n\n function strategyDisplayMetaText(platform, account, profile) {\n void profile;\n return t(\"strategyMeta\").replace(\"{domains}\", supportedDomainLabel(platform, account));\n }\n\n function strategyActionNoteText(platform = state.selected, account = selectedAccount(platform)) {\n const profile = state.forms[platform]?.strategy || \"\";\n const meta = strategyDisplayMetaText(platform, account, profile);\n return meta ? `${t(\"invalidStrategyNote\")}\\n${meta}` : t(\"invalidStrategyNote\");\n }\n\n function strategyChoiceLabel(profile, platform = state.selected, account = selectedAccount(platform), executionMode = state.forms[platform]?.executionMode) {\n const label = strategyLabel(profile);\n const entry = strategyCatalogEntry(profile);\n const domain = entry.domain ? domainLabel(entry.domain) : \"\";\n if (!entry.profile) return label;\n if (normalizeExecutionMode(executionMode, false) === \"live\" && !strategyCanSwitchLive(entry)) {\n return domain ? `${label}(${domain})` : label;\n }\n if (entry.runtime_enabled === false) {\n return domain ? `${label}(${domain})` : label;\n }\n return label;\n }\n\n function strategyAllowedForAccount(platform, account, profile, executionMode = state.forms[platform]?.executionMode) {\n const cleanProfile = cleanStrategyProfile(profile);\n const catalogEntry = strategyCatalogEntry(cleanProfile);\n if (!catalogEntry.profile) return false;\n if (dcaConfigForStrategy(cleanProfile) && !platformSupportsDca(platform)) return false;\n if (!supportedDomainsForAccount(platform, account).includes(catalogEntry.domain)) return false;\n const mode = normalizeExecutionMode(executionMode, false);\n if (!supportedExecutionModesForPlatform(platform).includes(mode)) return false;\n if (mode === \"live\") return strategyCanSwitchLive(catalogEntry);\n const allowedModes = normalizeAllowedExecutionModes(catalogEntry.allowed_execution_modes);\n if (allowedModes.length && !allowedModes.includes(mode)) return false;\n return true;\n }\n\n function strategyChoicesForAccount(platform = state.selected, account = selectedAccount(platform), executionMode = state.forms[platform]?.executionMode) {\n const choices = strategyOptions.filter((profile) => strategyAllowedForAccount(platform, account, profile, executionMode));\n const addChoice = (value) => {\n const profile = cleanStrategyProfile(value);\n if (profile && !choices.includes(profile) && strategyAllowedForAccount(platform, account, profile, executionMode)) {\n choices.push(profile);\n }\n };\n return choices;\n }\n\n function hasLiveStrategyOption(platform = state.selected, account = selectedAccount(platform)) {\n return strategyOptions.some((profile) => strategyAllowedForAccount(platform, account, profile, \"live\"));\n }\n\n function strategyLabel(profile) {\n const labels = strategyLabels[profile] || localStrategyLabels[profile];\n if (!labels) return profile;\n return state.lang === \"zh\"\n ? (labels.zh || labels.en || profile)\n : (labels.en || labels.zh || profile);\n }\n\n function strategyLabelSet(profile, item) {\n const local = localStrategyLabels[profile] || {};\n const label = String(item?.label || item?.display_name || \"\").trim();\n const labelEn = String(item?.label_en || item?.display_name_en || \"\").trim();\n const labelZh = String(item?.label_zh || item?.display_name_zh || \"\").trim();\n return {\n zh: labelZh || local.zh || label || local.en || profile,\n en: labelEn || label || local.en || labelZh || local.zh || profile,\n };\n }\n\n function modeLabel(mode) {\n return mode === \"dry_run\" ? t(\"dryRun\") : mode === \"paper\" ? t(\"paper\") : t(\"live\");\n }\n\n function normalizePluginMode(value) {\n return pluginModes.includes(value) ? value : \"none\";\n }\n\n function pluginModeLabel(mode) {\n void mode;\n return t(\"pluginModeNone\");\n }\n\n function dcaConfigForStrategy(profile) {\n const cleanProfile = cleanStrategyProfile(profile);\n const catalog = strategyCatalog[cleanProfile] || {};\n if (catalog.dca_enabled === true) {\n return {\n defaultMode: normalizeDcaMode(catalog.dca_default_mode || \"fixed\"),\n defaultBaseInvestmentUsd: cleanDisplayPositiveNumber(catalog.dca_default_base_investment_usd) || \"1000\",\n };\n }\n return dcaProfileDefaults[cleanProfile] || null;\n }\n\n function dcaSupported(profile) {\n return Boolean(dcaConfigForStrategy(profile));\n }\n\n function normalizeDcaMode(value) {\n const mode = String(value || \"\").trim().toLowerCase();\n if (mode === \"smart_dca\") return \"smart\";\n if (mode === \"fixed_dca\" || mode === \"ordinary\" || mode === \"ordinary_dca\") return \"fixed\";\n return dcaModes.includes(mode) ? mode : \"fixed\";\n }\n\n function dcaModeLabel(mode) {\n return normalizeDcaMode(mode) === \"smart\" ? t(\"dcaModeSmart\") : t(\"dcaModeFixed\");\n }\n\n function normalizeRuntimeTargetMode(value) {\n return runtimeTargetModes.includes(value) ? value : \"current\";\n }\n\n function runtimeTargetModeLabel(mode) {\n if (mode === \"enabled\") return t(\"runtimeTargetEnabled\");\n if (mode === \"disabled\") return t(\"runtimeTargetDisabled\");\n return t(\"runtimeTargetCurrent\");\n }\n\n function runtimeTargetEnabledForAccount(platform, account) {\n return cleanOptionalBoolean(currentEntryForAccount(platform, account)?.runtime_target_enabled);\n }\n\n function runtimeTargetStateForAccount(platform = state.selected, account = selectedAccount(platform)) {\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return { known: false, enabled: null };\n const configured = cleanOptionalBoolean(entry.runtime_target_enabled);\n return { known: true, enabled: configured ?? true };\n }\n\n function runtimeTargetText(enabled) {\n return enabled ? t(\"runtimeTargetOn\") : t(\"runtimeTargetOff\");\n }\n\n function runtimeTargetTone(enabled) {\n return enabled ? \"enabled\" : \"disabled\";\n }\n\n function currentRuntimeTargetText(platform = state.selected, account = selectedAccount(platform)) {\n const target = runtimeTargetStateForAccount(platform, account);\n if (!target.known) return t(\"notRead\");\n return runtimeTargetText(target.enabled);\n }\n\n function currentRuntimeTargetTone(platform = state.selected, account = selectedAccount(platform)) {\n const target = runtimeTargetStateForAccount(platform, account);\n if (!target.known) return \"neutral\";\n return runtimeTargetTone(target.enabled);\n }\n\n function incomeLayerDefaultForStrategy(profile) {\n return incomeLayerDefaults[cleanStrategyProfile(profile)] || null;\n }\n\n function incomeLayerSupported(profile) {\n return Boolean(incomeLayerDefaultForStrategy(profile));\n }\n\n function normalizeIncomeLayerMode(value) {\n return incomeLayerModes.includes(value) ? value : \"current\";\n }\n\n function incomeLayerModeLabel(mode) {\n if (mode === \"enabled\") return t(\"incomeLayerEnabled\");\n if (mode === \"disabled\") return t(\"incomeLayerDisabled\");\n return t(\"incomeLayerCurrent\");\n }\n\n function optionOverlayDefaultForStrategy(profile) {\n return optionOverlayDefaults[cleanStrategyProfile(profile)] || null;\n }\n\n function optionOverlaySupported(profile) {\n return Boolean(optionOverlayDefaultForStrategy(profile));\n }\n\n function normalizeOptionOverlayMode(value) {\n return optionOverlayModes.includes(value) ? value : \"current\";\n }\n\n function optionOverlayModeLabel(mode) {\n if (mode === \"enabled\") return t(\"optionOverlayEnabled\");\n if (mode === \"disabled\") return t(\"optionOverlayDisabled\");\n return t(\"optionOverlayCurrent\");\n }\n\n function optionOverlayText(enabled) {\n return enabled ? t(\"optionOverlayOn\") : t(\"optionOverlayOff\");\n }\n\n function normalizeCashOnlyExecutionMode(value) {\n return cashOnlyExecutionModes.includes(value) ? value : \"current\";\n }\n\n function cashOnlyExecutionModeLabel(mode) {\n if (mode === \"enabled\") return t(\"cashOnlyExecutionNo\");\n if (mode === \"disabled\") return t(\"cashOnlyExecutionYes\");\n return t(\"cashOnlyExecutionCurrent\");\n }\n\n function cashOnlyExecutionText(enabled) {\n if (enabled === null) return t(\"notRead\");\n return enabled ? t(\"cashOnlyExecutionValueNo\") : t(\"cashOnlyExecutionValueYes\");\n }\n\n function platformCashOnlyExecutionDefault() {\n return true;\n }\n\n function effectiveCashOnlyExecutionForAccount(platform, account) {\n const configured = currentCashOnlyExecutionForAccount(platform, account);\n if (configured !== null) return configured;\n if (!platformSupportsMarginPolicy(platform)) return null;\n return platformCashOnlyExecutionDefault();\n }\n\n function currentCashOnlyExecutionForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n if (entry) {\n const val = cleanOptionalBoolean(entry.cash_only_execution);\n if (val !== null) return val;\n }\n return platformCashOnlyExecutionDefault();\n }\n\n function currentCashOnlyExecutionText(platform = state.selected, account = selectedAccount(platform)) {\n if (!platformSupportsMarginPolicy(platform)) return t(\"notRead\");\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const configured = cleanOptionalBoolean(entry.cash_only_execution);\n if (configured === null) return t(\"cashOnlyExecutionDefault\");\n return cashOnlyExecutionText(configured);\n }\n\n function currentOptionOverlayForAccount(platform, account) {\n return cleanOptionalBoolean(currentEntryForAccount(platform, account)?.option_overlay_enabled);\n }\n\n function effectiveOptionOverlayForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const configured = currentOptionOverlayForAccount(platform, account);\n if (configured !== null) return configured;\n if (!optionOverlaySupported(profile)) return null;\n return true;\n }\n\n function optionOverlayDefaultSummaryDetail(defaults) {\n if (!defaults?.families?.length) return \"\";\n return defaults.families.map((item) => {\n const family = item.family === \"income\" ? t(\"optionOverlayFamilyIncome\") : t(\"optionOverlayFamilyGrowth\");\n const ratioText = item.ratioKind === \"risk\"\n ? t(\"optionOverlayRiskRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio))\n : t(\"optionOverlayBudgetRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio));\n return `${family} ${ratioText}`;\n }).join(\" / \");\n }\n\n function optionOverlayDefaultText(profile) {\n const defaults = optionOverlayDefaultForStrategy(profile);\n if (!defaults) return t(\"optionOverlayNotSupported\");\n const detail = optionOverlayDefaultSummaryDetail(defaults);\n return detail ? t(\"optionOverlayDefault\").replace(\"{detail}\", detail) : t(\"optionOverlayDefaultSimple\");\n }\n\n function currentOptionOverlayText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const configured = cleanOptionalBoolean(entry.option_overlay_enabled);\n if (!optionOverlaySupported(profile)) {\n return configured === null ? t(\"optionOverlayNotSupported\") : optionOverlayText(configured);\n }\n if (configured === null) return optionOverlayDefaultText(profile);\n return optionOverlayText(configured);\n }\n\n function currentIncomeLayerForAccount(platform, account) {\n return incomeLayerFromEntry(currentEntryForAccount(platform, account));\n }\n\n function incomeLayerFromEntry(entry) {\n return {\n enabled: cleanOptionalBoolean(entry?.income_layer_enabled),\n startUsd: cleanDisplayNumber(entry?.income_layer_start_usd),\n maxRatio: cleanDisplayRatio(entry?.income_layer_max_ratio),\n };\n }\n\n function incomeLayerFieldsConfigured(entry) {\n const current = incomeLayerFromEntry(entry);\n return current.enabled !== null || Boolean(current.startUsd || current.maxRatio);\n }\n\n function effectiveIncomeLayerForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return null;\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return null;\n const current = incomeLayerFromEntry(entry);\n if (!incomeLayerFieldsConfigured(entry)) {\n return {\n enabled: true,\n startUsd: String(defaults.startUsd),\n maxRatio: defaults.maxRatio,\n };\n }\n return {\n enabled: current.enabled ?? true,\n startUsd: current.startUsd || String(defaults.startUsd),\n maxRatio: current.maxRatio || defaults.maxRatio,\n };\n }\n\n function currentDcaForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const defaults = dcaConfigForStrategy(profile);\n if (!defaults) return { supported: false, mode: \"\", baseInvestmentUsd: \"\" };\n const entry = currentEntryForAccount(platform, account);\n return {\n supported: true,\n mode: normalizeDcaMode(entry?.dca_mode || account?.dca_mode || defaults.defaultMode),\n baseInvestmentUsd: cleanDisplayPositiveNumber(entry?.dca_base_investment_usd) ||\n cleanDisplayPositiveNumber(account?.dca_base_investment_usd) ||\n defaults.defaultBaseInvestmentUsd,\n };\n }\n\n function normalizeAccountLookupKey(value) {\n return String(value || \"\").trim().toLowerCase();\n }\n\n function collectAccountLookupCandidates(keys) {\n const candidates = new Set();\n for (const rawKey of keys) {\n const key = normalizeAccountLookupKey(rawKey);\n if (!key) continue;\n\n candidates.add(key);\n\n const compact = key.replace(/[^a-z0-9]+/g, \"\");\n if (compact) candidates.add(compact);\n\n const parts = key.split(/[^a-z0-9]+/).filter(Boolean);\n if (parts.length > 1) candidates.add(parts[parts.length - 1]);\n }\n return [...candidates];\n }\n\n function resolveCurrentEntryByKey(byPlatform, keys) {\n const candidates = new Set(collectAccountLookupCandidates(keys));\n if (!candidates.size) return null;\n\n for (const key of keys) {\n const entry = byPlatform[key];\n if (currentEntryHasState(entry)) return entry;\n }\n\n for (const [rawKey, entry] of Object.entries(byPlatform)) {\n if (!currentEntryHasState(entry)) continue;\n const rawCandidates = collectAccountLookupCandidates([rawKey]);\n const hasMatch = rawCandidates.some((candidate) => candidates.has(candidate));\n if (hasMatch) return entry;\n }\n\n return null;\n }\n\n function currentEntryForAccount(platform, account) {\n const byPlatform = state.currentStrategies[platform] || {};\n const keys = [account?.key, account?.target_name, account?.label]\n .filter(Boolean)\n .map((value) => String(value));\n const entry = resolveCurrentEntryByKey(byPlatform, keys);\n if (entry) return entry;\n const globalDefaults = window.__DEFAULT_ACCOUNT_OPTIONS__?.[platform]?.[0] || {};\n const merged = { ...globalDefaults, ...(account || {}) };\n const synth = {\n strategy_profile: \"\",\n source: \"account_defaults\",\n };\n const cashMode = merged.cash_only_execution_mode;\n if (cashMode === \"enabled\") synth.cash_only_execution = true;\n else if (cashMode === \"disabled\") synth.cash_only_execution = false;\n else if (platformSupportsMarginPolicy(platform)) synth.cash_only_execution = true;\n if (merged.min_reserved_cash_usd) synth.min_reserved_cash_usd = merged.min_reserved_cash_usd;\n if (merged.reserved_cash_ratio) synth.reserved_cash_ratio = merged.reserved_cash_ratio;\n synth.runtime_target_enabled = merged.runtime_target_enabled !== false;\n const execMode = merged.default_execution_mode || platformConfig[platform]?.default_execution_mode || \"live\";\n synth.execution_mode = execMode;\n synth.dry_run_only = execMode === \"paper\";\n return synth;\n }\n\n function currentEntryHasState(entry) {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return false;\n return Boolean(\n cleanStrategyProfile(entry.strategy_profile) ||\n cleanDisplayNumber(entry.min_reserved_cash_usd ?? entry.reserved_cash_floor_usd) ||\n cleanDisplayRatio(entry.reserved_cash_ratio) ||\n cleanOptionalBoolean(entry.income_layer_enabled) !== null ||\n cleanDisplayNumber(entry.income_layer_start_usd) ||\n cleanDisplayRatio(entry.income_layer_max_ratio) ||\n cleanOptionalBoolean(entry.option_overlay_enabled) !== null ||\n cleanOptionalBoolean(entry.cash_only_execution) !== null ||\n cleanOptionalBoolean(entry.runtime_target_enabled) !== null ||\n normalizeDcaMode(entry.dca_mode || \"\") !== \"fixed\" ||\n cleanDisplayPositiveNumber(entry.dca_base_investment_usd) ||\n normalizeExecutionMode(entry.execution_mode, entry.dry_run_only),\n );\n }\n\n function currentStrategyForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n return cleanStrategyProfile(entry?.strategy_profile) || \"\";\n }\n\n function currentReservePolicyForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n return reservePolicyFromEntry(entry);\n }\n\n function currentPluginModeForAccount(platform, account) {\n void platform;\n return normalizePluginMode(account?.plugin_mode);\n }\n\n function reservePolicyFromEntry(entry) {\n return {\n minReservedCashUsd: cleanDisplayNumber(entry?.min_reserved_cash_usd ?? entry?.reserved_cash_floor_usd),\n reservedCashRatio: cleanDisplayRatio(entry?.reserved_cash_ratio),\n };\n }\n\n function cleanDisplayNumber(value) {\n const text = String(value ?? \"\").trim();\n if (!text || text.length > 32 || !/^(?:\\d+|\\d*\\.\\d+)$/.test(text)) return \"\";\n const numeric = Number(text);\n if (!Number.isFinite(numeric) || numeric < 0) return \"\";\n return text;\n }\n\n function cleanDisplayRatio(value) {\n const text = cleanDisplayNumber(value);\n if (!text) return \"\";\n const numeric = Number(text);\n return numeric >= 0 && numeric <= 1 ? text : \"\";\n }\n\n function cleanDisplayPositiveNumber(value) {\n const text = cleanDisplayNumber(value);\n return text && Number(text) > 0 ? text : \"\";\n }\n\n function normalizeExecutionMode(value, dryRunOnly) {\n const mode = String(value || \"\").trim().toLowerCase();\n if (mode === \"live\") return \"live\";\n if (mode === \"paper\" || mode === \"dry_run\" || mode === \"dry-run\") return \"dry_run\";\n if (dryRunOnly === true || dryRunOnly === \"true\" || dryRunOnly === \"1\" || dryRunOnly === 1) return \"dry_run\";\n if (dryRunOnly === false || dryRunOnly === \"false\" || dryRunOnly === \"0\" || dryRunOnly === 0) return \"live\";\n return \"\";\n }\n\n function cleanOptionalBoolean(value) {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n if (typeof value === \"string\") {\n const text = value.trim().toLowerCase();\n if (text === \"true\" || text === \"1\") return true;\n if (text === \"false\" || text === \"0\") return false;\n }\n return null;\n }\n\n function defaultExecutionModeForAccount(platform, account, fallback = \"live\") {\n if (platformDryRunOnly(platform)) return \"dry_run\";\n const currentMode = normalizeExecutionMode(\n currentEntryForAccount(platform, account)?.execution_mode,\n currentEntryForAccount(platform, account)?.dry_run_only,\n );\n if (currentMode) return currentMode;\n const hint = [\n account?.key,\n account?.label,\n account?.target_name,\n account?.deployment_selector,\n account?.account_scope,\n account?.service_name,\n ].join(\" \").toLowerCase();\n if (hint.split(/\\s+/).includes(\"paper\") || hint.includes(\"-paper\") || hint.includes(\"_paper\") || hint.includes(\"dry_run\") || hint.includes(\"dry-run\")) {\n return \"dry_run\";\n }\n return fallback;\n }\n\n function defaultStrategyForAccount(platform, account) {\n const currentProfile = currentStrategyForAccount(platform, account);\n if (currentProfile) return currentProfile;\n return \"\";\n }\n\n function syncStrategyForAccount(platform) {\n const account = selectedAccount(platform);\n if (!account) return;\n state.forms[platform].strategy = defaultStrategyForAccount(platform, account);\n state.forms[platform].executionMode = defaultExecutionModeForAccount(\n platform,\n account,\n );\n state.forms[platform].pluginMode = currentPluginModeForAccount(platform, account);\n syncRuntimeTargetForAccount(platform);\n syncReservePolicyForAccount(platform);\n syncIncomeLayerForAccount(platform);\n syncOptionOverlayForAccount(platform);\n syncCashOnlyExecutionForAccount(platform);\n reconcileExecutionCashPolicy(state.forms[platform], \"margin\");\n syncDcaForAccount(platform);\n }\n\n function syncRuntimeTargetForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.runtimeTargetTouched) return;\n const current = runtimeTargetEnabledForAccount(platform, selectedAccount(platform));\n form.runtimeTargetMode = current === false ? \"disabled\" : \"enabled\";\n }\n\n function syncReservePolicyForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.reservedCashTouched) return;\n const policy = currentReservePolicyForAccount(platform, selectedAccount(platform));\n const hasFloor = Boolean(policy.minReservedCashUsd);\n const hasRatio = Boolean(policy.reservedCashRatio);\n if (hasFloor && hasRatio) form.reservePolicyMode = \"max\";\n else if (hasFloor) form.reservePolicyMode = \"floor\";\n else if (hasRatio) form.reservePolicyMode = \"ratio\";\n else form.reservePolicyMode = \"none\";\n form.minReservedCashUsd = policy.minReservedCashUsd;\n form.reservedCashRatio = policy.reservedCashRatio;\n }\n\n function syncIncomeLayerForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.incomeLayerTouched) return;\n const defaults = incomeLayerDefaultForStrategy(form.strategy);\n const current = currentIncomeLayerForAccount(platform, selectedAccount(platform));\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n if (entry && incomeLayerFieldsConfigured(entry)) {\n form.incomeLayerMode = current.enabled === false ? \"disabled\" : \"enabled\";\n } else {\n form.incomeLayerMode = incomeLayerSupported(form.strategy) ? \"enabled\" : \"disabled\";\n }\n form.incomeLayerStartUsd = current.startUsd || String(defaults?.startUsd || \"\");\n form.incomeLayerMaxRatio = current.maxRatio || defaults?.maxRatio || \"\";\n }\n\n function syncOptionOverlayForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.optionOverlayTouched) return;\n const configured = normalizeOptionOverlayMode(selectedAccount(platform)?.option_overlay_mode);\n if (configured !== \"current\") {\n form.optionOverlayMode = configured;\n return;\n }\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n const rawValue = cleanOptionalBoolean(entry?.option_overlay_enabled);\n if (rawValue !== null) {\n form.optionOverlayMode = rawValue ? \"enabled\" : \"disabled\";\n return;\n }\n form.optionOverlayMode = optionOverlaySupported(form.strategy) ? \"enabled\" : \"disabled\";\n }\n\n function syncCashOnlyExecutionForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.cashOnlyExecutionTouched) return;\n const configured = normalizeCashOnlyExecutionMode(selectedAccount(platform)?.cash_only_execution_mode);\n if (configured !== \"current\") {\n form.cashOnlyExecutionMode = configured;\n return;\n }\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n const rawValue = cleanOptionalBoolean(entry?.cash_only_execution);\n if (rawValue !== null) {\n form.cashOnlyExecutionMode = rawValue ? \"enabled\" : \"disabled\";\n return;\n }\n // No explicit config — use platform default (cash-only for margin-capable platforms)\n form.cashOnlyExecutionMode = platformSupportsMarginPolicy(platform) ? \"enabled\" : \"disabled\";\n }\n\n function syncDcaForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.dcaTouched) return;\n const current = currentDcaForAccount(platform, selectedAccount(platform), form.strategy);\n form.dcaMode = current.supported ? current.mode : \"fixed\";\n form.dcaBaseInvestmentUsd = current.supported ? current.baseInvestmentUsd : \"\";\n }\n\n function ensureAccountSelection(platform) {\n const options = optionsFor(platform);\n if (!options.length) return;\n if (!options.some((option) => option.key === state.forms[platform].accountKey)) {\n state.forms[platform].accountKey = options[0].key;\n state.forms[platform].runtimeTargetTouched = false;\n state.forms[platform].reservedCashTouched = false;\n state.forms[platform].incomeLayerTouched = false;\n state.forms[platform].optionOverlayTouched = false;\n state.forms[platform].cashOnlyExecutionTouched = false;\n state.forms[platform].dcaTouched = false;\n state.forms[platform].strategy = defaultStrategyForAccount(platform, options[0]);\n state.forms[platform].pluginMode = currentPluginModeForAccount(platform, options[0]);\n syncRuntimeTargetForAccount(platform);\n syncReservePolicyForAccount(platform);\n syncIncomeLayerForAccount(platform);\n syncOptionOverlayForAccount(platform);\n syncCashOnlyExecutionForAccount(platform);\n syncDcaForAccount(platform);\n }\n }\n\n function derivedService(platform, targetName) {\n if (platform === \"longbridge\") return `longbridge-quant-${targetName.toLowerCase()}-service`;\n if (platform === \"ibkr\") return `interactive-brokers-${targetName.toLowerCase()}-service`;\n if (platform === \"schwab\") return \"charles-schwab-quant-service\";\n if (platform === \"firstrade\") return \"firstrade-quant-service\";\n if (platform === \"qmt\") return \"qmt-quant-service\";\n return \"\";\n }\n\n function accountMetaText(platform = state.selected) {\n const account = selectedAccount(platform);\n const targetName = account.target_name || account.key;\n const raw = account.service_name || derivedService(platform, targetName);\n const service = raw || (state.lang === \"zh\" ? \"无\" : \"-\");\n return t(\"targetMeta\")\n .replace(\"{target}\", targetName)\n .replace(\"{service}\", service)\n .replace(\"{domains}\", supportedDomainLabel(platform, account));\n }\n\n function hasRunnableStrategySelection(platform = state.selected) {\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n return Boolean(form?.strategy && account && strategyAllowedForAccount(platform, account, form.strategy, form.executionMode));\n }\n\n function hasValidReservePolicy(platform = state.selected) {\n if (!platformSupportsReservedCashPolicy(platform)) return true;\n const form = state.forms[platform];\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n if (mode === \"current\" || mode === \"none\") return true;\n return Boolean(reservePolicyOverrideForForm(form, platform));\n }\n\n function hasValidExecutionCashPolicy(platform = state.selected) {\n if (!platformSupportsMarginPolicy(platform) && !platformSupportsReservedCashPolicy(platform)) return true;\n const form = state.forms[platform];\n return !executionCashPolicyConflict(form) && hasValidReservePolicy(platform);\n }\n\n function hasValidIncomeLayerPolicy(platform = state.selected) {\n const form = state.forms[platform];\n if (!incomeLayerSupported(form?.strategy)) return true;\n const mode = normalizeIncomeLayerMode(form?.incomeLayerMode);\n if (mode === \"current\" || mode === \"disabled\") return true;\n const defaults = incomeLayerDefaultForStrategy(form?.strategy);\n const startUsd = cleanDisplayNumber(form?.incomeLayerStartUsd || defaults?.startUsd);\n const maxRatio = cleanDisplayRatio(form?.incomeLayerMaxRatio || defaults?.maxRatio);\n return Boolean(startUsd && maxRatio);\n }\n\n function hasValidOptionOverlayPolicy(platform = state.selected) {\n const form = state.forms[platform];\n const mode = normalizeOptionOverlayMode(form?.optionOverlayMode);\n return mode !== \"enabled\" || optionOverlaySupported(form?.strategy);\n }\n\n function hasValidDcaPolicy(platform = state.selected) {\n const form = state.forms[platform];\n if (!dcaSupported(form?.strategy) || !platformSupportsDca(platform)) return true;\n return Boolean(dcaModes.includes(normalizeDcaMode(form?.dcaMode)) && cleanDisplayPositiveNumber(form?.dcaBaseInvestmentUsd));\n }\n\n function hasValidStrategySelection(platform = state.selected) {\n return hasRunnableStrategySelection(platform) &&\n hasValidExecutionCashPolicy(platform) &&\n hasValidIncomeLayerPolicy(platform) &&\n hasValidOptionOverlayPolicy(platform) &&\n hasValidDcaPolicy(platform);\n }\n\n function normalizeReservePolicyMode(value) {\n return reservePolicyModes.includes(value) ? value : \"current\";\n }\n\n function reservePolicyOverrideForForm(form, platform = state.selected) {\n if (!platformSupportsReservedCashPolicy(platform)) return null;\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n const floor = cleanDisplayNumber(form?.minReservedCashUsd);\n const ratio = cleanDisplayRatio(form?.reservedCashRatio);\n const extraVariables = {};\n if (mode === \"current\") return null;\n if (mode === \"none\") {\n extraVariables[platformMinReservedCashVariables[platform]] = \"\";\n extraVariables[platformReservedCashRatioVariables[platform]] = \"\";\n return { inputs: {}, extraVariables };\n }\n if (mode === \"ratio\") {\n if (!ratio) return null;\n extraVariables[platformMinReservedCashVariables[platform]] = \"\";\n return { inputs: { reserved_cash_ratio: ratio }, extraVariables };\n }\n if (mode === \"floor\") {\n if (!floor) return null;\n extraVariables[platformReservedCashRatioVariables[platform]] = \"\";\n return { inputs: { min_reserved_cash_usd: floor }, extraVariables };\n }\n if (mode === \"max\") {\n if (!floor || !ratio) return null;\n return { inputs: { min_reserved_cash_usd: floor, reserved_cash_ratio: ratio }, extraVariables };\n }\n return null;\n }\n\n function runtimeTargetOverrideForForm(form) {\n const mode = normalizeRuntimeTargetMode(form?.runtimeTargetMode);\n if (mode === \"current\") return null;\n return {\n inputs: { runtime_target_enabled_mode: mode },\n extraVariables: { [runtimeTargetEnabledVariable]: mode === \"enabled\" ? \"true\" : \"false\" },\n };\n }\n\n function incomeLayerOverrideForForm(form) {\n const defaults = incomeLayerDefaultForStrategy(form?.strategy);\n if (!defaults) return null;\n const mode = normalizeIncomeLayerMode(form?.incomeLayerMode);\n if (mode === \"current\") return null;\n const extraVariables = {};\n if (mode === \"disabled\") {\n extraVariables[incomeLayerEnabledVariable] = \"false\";\n extraVariables[incomeLayerStartUsdVariable] = \"\";\n extraVariables[incomeLayerMaxRatioVariable] = \"\";\n return { inputs: { income_layer_mode: mode }, extraVariables };\n }\n const startUsd = cleanDisplayNumber(form?.incomeLayerStartUsd || defaults.startUsd);\n const maxRatio = cleanDisplayRatio(form?.incomeLayerMaxRatio || defaults.maxRatio);\n if (!startUsd || !maxRatio) return null;\n extraVariables[incomeLayerEnabledVariable] = \"true\";\n extraVariables[incomeLayerStartUsdVariable] = startUsd;\n extraVariables[incomeLayerMaxRatioVariable] = maxRatio;\n return { inputs: { income_layer_mode: mode, income_layer_start_usd: startUsd, income_layer_max_ratio: maxRatio }, extraVariables };\n }\n\n function optionOverlayOverrideForForm(form) {\n const mode = normalizeOptionOverlayMode(form?.optionOverlayMode);\n if (mode === \"current\") return null;\n if (mode === \"enabled\" && !optionOverlaySupported(form?.strategy)) return null;\n return { inputs: { option_overlay_mode: mode } };\n }\n\n function cashOnlyExecutionOverrideForForm(form, platform = state.selected) {\n if (!platformSupportsMarginPolicy(platform)) return null;\n const mode = normalizeCashOnlyExecutionMode(form?.cashOnlyExecutionMode);\n if (mode === \"current\") return null;\n return { inputs: { cash_only_execution_mode: mode } };\n }\n\n function dcaOverrideForForm(form) {\n if (!dcaSupported(form?.strategy) || !platformSupportsDca(state.selected)) return null;\n const mode = normalizeDcaMode(form?.dcaMode);\n const baseInvestmentUsd = cleanDisplayPositiveNumber(form?.dcaBaseInvestmentUsd);\n if (!baseInvestmentUsd) return null;\n return { inputs: { dca_mode: mode, dca_base_investment_usd: baseInvestmentUsd } };\n }\n\n function mergeExtraVariables(inputs, extraVariables) {\n if (!extraVariables || !Object.keys(extraVariables).length) return;\n const merged = inputs.extra_variables_json ? JSON.parse(inputs.extra_variables_json) : {};\n Object.assign(merged, extraVariables);\n inputs.extra_variables_json = JSON.stringify(merged);\n }\n\n function buildInputs(platform = state.selected) {\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n const targetName = account.target_name || account.key;\n const inputs = {\n platform,\n target_name: targetName,\n strategy_profile: form.strategy,\n execution_mode: form.executionMode,\n variable_scope: account.variable_scope || \"default\",\n plugin_mode: normalizePluginMode(form.pluginMode),\n service_targets_mode: \"auto\",\n apply: \"true\",\n trigger_platform_sync: \"true\",\n confirm_apply: \"APPLY_AND_SYNC\",\n platform_sync_workflow: \"sync-cloud-run-env.yml\",\n };\n for (const field of [\n \"github_environment\",\n \"deployment_selector\",\n \"account_selector\",\n \"account_scope\",\n \"service_name\",\n ]) {\n if (account[field]) inputs[field] = account[field];\n }\n const reserveOverride = platformSupportsReservedCashPolicy(platform)\n ? reservePolicyOverrideForForm(form, platform)\n : null;\n if (platformSupportsReservedCashPolicy(platform)) {\n inputs.reserved_cash_policy_mode = normalizeReservePolicyMode(form.reservePolicyMode);\n if (reserveOverride) {\n Object.assign(inputs, reserveOverride.inputs);\n mergeExtraVariables(inputs, reserveOverride.extraVariables);\n }\n }\n const runtimeTargetOverride = runtimeTargetOverrideForForm(form);\n inputs.runtime_target_enabled_mode = normalizeRuntimeTargetMode(form.runtimeTargetMode);\n if (runtimeTargetOverride) {\n Object.assign(inputs, runtimeTargetOverride.inputs);\n mergeExtraVariables(inputs, runtimeTargetOverride.extraVariables);\n }\n const incomeOverride = incomeLayerOverrideForForm(form);\n inputs.income_layer_mode = normalizeIncomeLayerMode(form.incomeLayerMode);\n if (incomeOverride) {\n Object.assign(inputs, incomeOverride.inputs);\n mergeExtraVariables(inputs, incomeOverride.extraVariables);\n }\n const optionOverlayOverride = optionOverlayOverrideForForm(form);\n inputs.option_overlay_mode = normalizeOptionOverlayMode(form.optionOverlayMode);\n if (optionOverlayOverride) {\n Object.assign(inputs, optionOverlayOverride.inputs);\n }\n const cashOnlyOverride = cashOnlyExecutionOverrideForForm(form, platform);\n if (platformSupportsMarginPolicy(platform)) {\n inputs.cash_only_execution_mode = normalizeCashOnlyExecutionMode(form.cashOnlyExecutionMode);\n if (cashOnlyOverride) {\n Object.assign(inputs, cashOnlyOverride.inputs);\n }\n }\n const dcaOverride = dcaOverrideForForm(form);\n if (dcaOverride) {\n Object.assign(inputs, dcaOverride.inputs);\n }\n return inputs;\n }\n\n function reservedCashPolicyText(inputs, platform = state.selected, account = selectedAccount(platform), fallback = t(\"unchanged\")) {\n if (inputs?.reserved_cash_policy_mode === \"none\") return t(\"reservedCashNone\");\n const floor = cleanDisplayNumber(inputs?.min_reserved_cash_usd);\n const ratio = cleanDisplayRatio(inputs?.reserved_cash_ratio);\n const currency = selectedCashCurrency(platform, account);\n const hasEffectiveFloor = Boolean(floor && !(ratio && Number(floor) === 0));\n if (!hasEffectiveFloor && !ratio) return fallback;\n if (hasEffectiveFloor && ratio) return `max(${floor} ${currency}, ${formatRatioPercent(ratio)})`;\n if (hasEffectiveFloor) return `${floor} ${currency}`;\n return formatRatioPercent(ratio);\n }\n\n function platformReservedCashDefaultText(platform = state.selected, account = selectedAccount(platform)) {\n return t(\"reservedCashDefault\").replace(\"{currency}\", selectedCashCurrency(platform, account));\n }\n\n function currentReservedCashPolicyText(platform = state.selected, account = selectedAccount(platform)) {\n const entry = currentEntryForAccount(platform, account);\n const policy = currentReservePolicyForAccount(platform, account);\n return reservedCashPolicyText(\n {\n min_reserved_cash_usd: policy.minReservedCashUsd,\n reserved_cash_ratio: policy.reservedCashRatio,\n },\n platform,\n account,\n entry ? platformReservedCashDefaultText(platform, account) : t(\"notRead\"),\n );\n }\n\n function pendingReservedCashPolicyText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n return reservedCashPolicyText(pendingReservePolicy(inputs, platform, account).inputs, platform, account, t(\"unchanged\"));\n }\n\n function pendingReservePolicy(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const current = currentReservePolicyForAccount(platform, account);\n const currentFloor = cleanDisplayNumber(current.minReservedCashUsd);\n const currentRatio = cleanDisplayRatio(current.reservedCashRatio);\n const mode = normalizeReservePolicyMode(inputs.reserved_cash_policy_mode);\n const next = {\n min_reserved_cash_usd: cleanDisplayNumber(inputs.min_reserved_cash_usd),\n reserved_cash_ratio: cleanDisplayRatio(inputs.reserved_cash_ratio),\n };\n if (mode === \"none\") {\n next.reserved_cash_policy_mode = \"none\";\n }\n const entry = currentEntryForAccount(platform, account);\n const changed = Boolean(entry && (\n next.min_reserved_cash_usd !== currentFloor ||\n next.reserved_cash_ratio !== currentRatio ||\n (mode === \"none\" && (currentFloor || currentRatio))\n ));\n return { changed, inputs: next };\n }\n\n function currentIncomeLayerText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return t(\"incomeLayerNotSupported\");\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const current = incomeLayerFromEntry(entry);\n if (!incomeLayerFieldsConfigured(entry)) {\n return t(\"incomeLayerDefault\")\n .replace(\"{start}\", formatUsd(defaults.startUsd))\n .replace(\"{ratio}\", formatRatioPercent(defaults.maxRatio));\n }\n const enabled = current.enabled ?? true;\n const startUsd = current.startUsd || String(defaults.startUsd);\n const ratio = current.maxRatio || defaults.maxRatio;\n return enabled\n ? t(\"incomeLayerOn\")\n .replace(\"{start}\", formatUsd(startUsd))\n .replace(\"{ratio}\", formatRatioPercent(ratio))\n : t(\"incomeLayerOff\");\n }\n\n function pendingIncomeLayerText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingIncomeLayer(inputs, platform, account);\n if (!pending.supported) return t(\"incomeLayerNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n if (pending.inputs.income_layer_enabled === false) return t(\"incomeLayerOff\");\n return t(\"incomeLayerOn\")\n .replace(\"{start}\", formatUsd(pending.inputs.income_layer_start_usd))\n .replace(\"{ratio}\", formatRatioPercent(pending.inputs.income_layer_max_ratio));\n }\n\n function pendingOptionOverlayText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingOptionOverlay(inputs, platform, account);\n if (!pending.supported && pending.inputs.option_overlay_enabled !== false) return t(\"optionOverlayNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n return optionOverlayText(pending.inputs.option_overlay_enabled);\n }\n\n function pendingCashOnlyExecutionText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingCashOnlyExecution(inputs, platform, account);\n if (!pending.changed) return t(\"unchanged\");\n return cashOnlyExecutionText(pending.inputs.cash_only_execution);\n }\n\n function pendingRuntimeTargetText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingRuntimeTarget(inputs, platform, account);\n if (!pending.changed) return t(\"unchanged\");\n return runtimeTargetText(pending.inputs.runtime_target_enabled);\n }\n\n function pendingRuntimeTargetTone(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingRuntimeTarget(inputs, platform, account);\n if (!pending.changed) return \"neutral\";\n return runtimeTargetTone(pending.inputs.runtime_target_enabled);\n }\n\n function currentDcaText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const current = currentDcaForAccount(platform, account, profile);\n if (!current.supported) return t(\"dcaNotSupported\");\n return t(\"dcaText\")\n .replace(\"{mode}\", dcaModeLabel(current.mode))\n .replace(\"{amount}\", formatUsd(current.baseInvestmentUsd));\n }\n\n function pendingDcaText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingDca(inputs, platform, account);\n if (!pending.supported) return t(\"dcaNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n return t(\"dcaText\")\n .replace(\"{mode}\", dcaModeLabel(pending.inputs.dca_mode))\n .replace(\"{amount}\", formatUsd(pending.inputs.dca_base_investment_usd));\n }\n\n function pendingRuntimeTarget(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const mode = normalizeRuntimeTargetMode(inputs.runtime_target_enabled_mode);\n if (mode === \"current\") {\n return {\n changed: false,\n inputs: { runtime_target_enabled: runtimeTargetEnabledForAccount(platform, account) ?? true },\n };\n }\n const current = runtimeTargetEnabledForAccount(platform, account);\n const currentEnabled = current ?? true;\n const nextEnabled = mode === \"enabled\";\n const entry = currentEntryForAccount(platform, account);\n return {\n changed: Boolean(entry && current !== null && currentEnabled !== nextEnabled),\n inputs: { runtime_target_enabled: nextEnabled },\n };\n }\n\n function pendingIncomeLayer(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return { supported: false, changed: false, inputs: {} };\n const mode = normalizeIncomeLayerMode(inputs.income_layer_mode);\n const entry = currentEntryForAccount(platform, account);\n const rawCurrent = currentIncomeLayerForAccount(platform, account);\n const effective = effectiveIncomeLayerForAccount(platform, account, profile);\n const currentEnabled = effective?.enabled ?? true;\n const currentStartUsd = effective?.startUsd ?? String(defaults.startUsd);\n const currentRatio = effective?.maxRatio ?? defaults.maxRatio;\n if (mode === \"current\") {\n return {\n supported: true,\n changed: false,\n inputs: {\n income_layer_enabled: rawCurrent.enabled,\n income_layer_start_usd: rawCurrent.startUsd,\n income_layer_max_ratio: rawCurrent.maxRatio,\n },\n };\n }\n if (mode === \"disabled\") {\n if (!entry) {\n return {\n supported: true,\n changed: false,\n inputs: {\n income_layer_enabled: false,\n income_layer_start_usd: \"\",\n income_layer_max_ratio: \"\",\n },\n };\n }\n return {\n supported: true,\n changed: currentEnabled !== false || Boolean(rawCurrent.startUsd || rawCurrent.maxRatio),\n inputs: {\n income_layer_enabled: false,\n income_layer_start_usd: \"\",\n income_layer_max_ratio: \"\",\n },\n };\n }\n const nextStartUsd = cleanDisplayNumber(inputs.income_layer_start_usd || defaults.startUsd);\n const nextRatio = cleanDisplayRatio(inputs.income_layer_max_ratio || defaults.maxRatio);\n return {\n supported: true,\n changed: Boolean(entry && (currentEnabled !== true || nextStartUsd !== currentStartUsd || nextRatio !== currentRatio)),\n inputs: {\n income_layer_enabled: true,\n income_layer_start_usd: nextStartUsd,\n income_layer_max_ratio: nextRatio,\n },\n };\n }\n\n function pendingOptionOverlay(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const supported = optionOverlaySupported(profile);\n const mode = normalizeOptionOverlayMode(inputs.option_overlay_mode);\n const current = effectiveOptionOverlayForAccount(platform, account, profile);\n if (mode === \"current\") {\n return {\n supported,\n changed: false,\n inputs: { option_overlay_enabled: currentOptionOverlayForAccount(platform, account) },\n };\n }\n if (mode === \"enabled\") {\n return {\n supported,\n changed: supported && current !== null && current !== true,\n inputs: { option_overlay_enabled: true },\n };\n }\n return {\n supported,\n changed: current === true,\n inputs: { option_overlay_enabled: false },\n };\n }\n\n function pendingCashOnlyExecution(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const mode = normalizeCashOnlyExecutionMode(inputs.cash_only_execution_mode);\n if (!platformSupportsMarginPolicy(platform) || mode === \"current\") {\n return { changed: false, inputs: {} };\n }\n const current = effectiveCashOnlyExecutionForAccount(platform, account);\n const nextEnabled = mode === \"enabled\";\n const entry = currentEntryForAccount(platform, account);\n return {\n changed: Boolean(entry && current !== null && current !== nextEnabled),\n inputs: { cash_only_execution: nextEnabled },\n };\n }\n\n function pendingDca(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const defaults = dcaConfigForStrategy(profile);\n if (!defaults) return { supported: false, changed: false, inputs: {} };\n const current = currentDcaForAccount(platform, account, profile);\n const nextMode = normalizeDcaMode(inputs.dca_mode || defaults.defaultMode);\n const nextBase = cleanDisplayPositiveNumber(inputs.dca_base_investment_usd || defaults.defaultBaseInvestmentUsd);\n return {\n supported: true,\n changed: Boolean(current.mode !== nextMode || current.baseInvestmentUsd !== nextBase),\n inputs: {\n dca_mode: nextMode,\n dca_base_investment_usd: nextBase,\n },\n };\n }\n\n function pendingChangeState(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const currentProfile = currentStrategyForAccount(platform, account);\n const nextProfile = cleanStrategyProfile(inputs.strategy_profile);\n const currentEntry = currentEntryForAccount(platform, account);\n const currentMode = normalizeExecutionMode(currentEntry?.execution_mode, currentEntry?.dry_run_only);\n const currentPluginMode = currentPluginModeForAccount(platform, account);\n const nextPluginMode = normalizePluginMode(inputs.plugin_mode);\n const runtimeTarget = pendingRuntimeTarget(inputs, platform, account);\n const reserve = pendingReservePolicy(inputs, platform, account);\n const income = pendingIncomeLayer(inputs, platform, account);\n const optionOverlay = pendingOptionOverlay(inputs, platform, account);\n const cashOnly = pendingCashOnlyExecution(inputs, platform, account);\n const dca = pendingDca(inputs, platform, account);\n return {\n currentProfile,\n nextProfile,\n currentMode,\n currentPluginMode,\n nextPluginMode,\n strategyChanged: Boolean(nextProfile && ((state.forms[platform]?.strategyTouched) || (currentProfile && currentProfile !== nextProfile))),\n modeChanged: Boolean(inputs.execution_mode && currentMode && currentMode !== inputs.execution_mode),\n pluginModeChanged: Boolean(nextPluginMode && currentPluginMode && currentPluginMode !== nextPluginMode),\n runtimeTargetChanged: runtimeTarget.changed,\n reserveCashChanged: reserve.changed,\n incomeLayerChanged: income.changed,\n optionOverlayChanged: optionOverlay.changed,\n cashOnlyChanged: cashOnly.changed,\n dcaChanged: dca.changed,\n runtimeTarget,\n reserve,\n income,\n optionOverlay,\n cashOnly,\n dca,\n };\n }\n\n function hasPendingChanges(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const changes = pendingChangeState(inputs, platform, account);\n return Boolean(\n changes.strategyChanged ||\n changes.modeChanged ||\n changes.pluginModeChanged ||\n changes.runtimeTargetChanged ||\n changes.reserveCashChanged ||\n changes.incomeLayerChanged ||\n changes.optionOverlayChanged ||\n changes.cashOnlyChanged ||\n changes.dcaChanged\n );\n }\n\n function formatRatioPercent(value) {\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return String(value);\n return `${(numeric * 100).toFixed(2).replace(/\\.?0+$/, \"\")}%`;\n }\n\n function formatUsd(value) {\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return String(value);\n return `$${numeric.toLocaleString(\"en-US\", { maximumFractionDigits: 0 })}`;\n }\n\n function incomeLayerAllocationText(defaults) {\n if (!defaults?.allocations) return \"\";\n return Object.entries(defaults.allocations)\n .map(([symbol, ratio]) => `${symbol} ${formatRatioPercent(ratio)}`)\n .join(\" / \");\n }\n\n function incomeLayerDefaultMetaText(defaults) {\n if (!defaults) return t(\"incomeLayerModeMeta\");\n return t(\"incomeLayerDefaultMeta\")\n .replace(\"{start}\", formatUsd(defaults.startUsd))\n .replace(\"{ratio}\", formatRatioPercent(defaults.maxRatio));\n }\n\n function optionOverlayDefaultMetaText(defaults) {\n if (!defaults?.families?.length) return t(\"optionOverlayModeMeta\");\n const familyText = defaults.families.map((item) => {\n const family = item.family === \"income\" ? t(\"optionOverlayFamilyIncome\") : t(\"optionOverlayFamilyGrowth\");\n const ratioText = item.ratioKind === \"risk\"\n ? t(\"optionOverlayRiskRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio))\n : t(\"optionOverlayBudgetRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio));\n return `${family}: ${item.recipe}, ${formatUsd(item.startUsd)}, ${ratioText}`;\n }).join(\" / \");\n return t(\"optionOverlayDefaultMeta\").replace(\"{defaults}\", familyText);\n }\n\n function summaryRows(inputs) {\n const account = selectedAccount();\n const changes = pendingChangeState(inputs, state.selected, account);\n const currentStrategyText = changes.currentProfile ? strategyLabel(changes.currentProfile) : t(\"notRead\");\n const rows = [\n [t(\"repository\"), state.repositories[state.selected] || defaultRepositories[state.selected]],\n [t(\"selectedAccount\"), account.label],\n [t(\"currentStrategy\"), currentStrategyText],\n [t(\"selectedMarket\"), supportedDomainLabel(state.selected, account)],\n [\n t(\"currentRuntimeTarget\"),\n currentRuntimeTargetText(state.selected, account),\n \"\",\n currentRuntimeTargetTone(state.selected, account),\n ],\n [t(\"currentPluginMode\"), pluginModeLabel(changes.currentPluginMode)],\n [t(\"reservedCashPolicy\"), currentReservedCashPolicyText(state.selected, account)],\n ];\n if (platformSupportsMarginPolicy(state.selected)) {\n rows.push([t(\"currentCashOnlyExecution\"), currentCashOnlyExecutionText(state.selected, account)]);\n }\n if (incomeLayerSupported(inputs.strategy_profile)) {\n rows.push([t(\"currentIncomeLayer\"), currentIncomeLayerText(state.selected, account, inputs.strategy_profile)]);\n }\n if (optionOverlaySupported(inputs.strategy_profile) || changes.optionOverlayChanged) {\n rows.push([t(\"currentOptionOverlay\"), currentOptionOverlayText(state.selected, account, inputs.strategy_profile)]);\n }\n if (dcaSupported(inputs.strategy_profile)) {\n rows.push([t(\"currentDca\"), currentDcaText(state.selected, account, inputs.strategy_profile)]);\n }\n if (changes.reserveCashChanged) {\n rows.push([t(\"pendingReservedCashPolicy\"), pendingReservedCashPolicyText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.incomeLayerChanged) {\n rows.push([t(\"pendingIncomeLayer\"), pendingIncomeLayerText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.optionOverlayChanged) {\n rows.push([t(\"pendingOptionOverlay\"), pendingOptionOverlayText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.cashOnlyChanged) {\n rows.push([t(\"pendingCashOnlyExecution\"), pendingCashOnlyExecutionText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.dcaChanged) {\n rows.push([t(\"pendingDca\"), pendingDcaText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.modeChanged) {\n rows.push([t(\"pendingMode\"), modeLabel(inputs.execution_mode), \"pending\"]);\n }\n if (changes.pluginModeChanged) {\n rows.push([t(\"pendingPluginMode\"), pluginModeLabel(changes.nextPluginMode), \"pending\"]);\n }\n if (changes.runtimeTargetChanged) {\n rows.push([\n t(\"pendingRuntimeTarget\"),\n pendingRuntimeTargetText(inputs, state.selected, account),\n \"pending\",\n pendingRuntimeTargetTone(inputs, state.selected, account),\n ]);\n }\n if (changes.strategyChanged && changes.nextProfile) {\n rows.push([t(\"nextStrategy\"), strategyLabel(changes.nextProfile), \"pending\"]);\n }\n return rows;\n }\n\n function applyLanguage() {\n document.documentElement.lang = state.lang === \"zh\" ? \"zh-CN\" : \"en\";\n document.querySelectorAll(\"[data-i18n]\").forEach((node) => {\n node.textContent = t(node.dataset.i18n);\n });\n el(\"lang-button\").textContent = state.lang === \"zh\" ? \"EN\" : \"中\";\n }\n\n function renderRuntimeAuthorityStatus() {\n const status = String(runtimeAuthorityStatus.status || \"UNKNOWN_RUNTIME_AUTHORITY_STATUS\");\n const asOf = String(runtimeAuthorityStatus.status_as_of || \"—\");\n el(\"runtime-authority-status\").textContent = status;\n el(\"runtime-authority-as-of\").textContent = t(\"runtimeAuthorityAsOf\").replace(\"{asOf}\", asOf);\n }\n\n function renderPlatforms() {\n const strip = el(\"platform-strip\");\n strip.replaceChildren();\n const showPrivateConfig = hasPrivateConfig();\n for (const platform of Object.keys(platformMeta)) {\n ensureAccountSelection(platform);\n const meta = platformMeta[platform];\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n const button = document.createElement(\"button\");\n button.className = \"platform-button\";\n button.type = \"button\";\n button.dataset.platform = platform;\n button.classList.toggle(\"active\", platform === state.selected);\n const mark = document.createElement(\"span\");\n mark.className = \"mark\";\n mark.textContent = meta.code;\n const copyNode = document.createElement(\"span\");\n copyNode.className = \"platform-copy\";\n const labelNode = document.createElement(\"strong\");\n labelNode.textContent = meta.label;\n copyNode.append(labelNode);\n if (showPrivateConfig) {\n const accountNode = document.createElement(\"span\");\n accountNode.textContent = account.label;\n const strategyNode = document.createElement(\"small\");\n strategyNode.textContent = strategyLabel(form.strategy);\n copyNode.append(accountNode, strategyNode);\n }\n button.append(mark, copyNode);\n strip.appendChild(button);\n }\n }\n\n function renderControls() {\n const platform = state.selected;\n const meta = platformMeta[platform];\n const form = state.forms[platform];\n const accounts = optionsFor(platform);\n const account = selectedAccount(platform);\n const choices = strategyChoicesForAccount(platform, account, form.executionMode);\n const currentStrategy = currentStrategyForAccount(platform, account);\n const currentStrategyBlocked = Boolean(\n currentStrategy &&\n !strategyAllowedForAccount(platform, account, currentStrategy, form.executionMode),\n );\n const accountSelect = el(\"account-select\");\n const strategySelect = el(\"strategy-select\");\n const runtimeTargetEnabledSelect = el(\"runtime-target-enabled-select\");\n const pluginModeSelect = el(\"plugin-mode-select\");\n const incomeLayerModeSelect = el(\"income-layer-mode-select\");\n const incomeLayerStartUsdInput = el(\"income-layer-start-usd-input\");\n const incomeLayerMaxRatioInput = el(\"income-layer-max-ratio-input\");\n const optionOverlayModeSelect = el(\"option-overlay-mode-select\");\n const cashOnlyExecutionModeSelect = el(\"cash-only-execution-mode-select\");\n const dcaModeSelect = el(\"dca-mode-select\");\n const dcaBaseInvestmentUsdInput = el(\"dca-base-investment-usd-input\");\n const reservePolicyModeSelect = el(\"reserve-policy-mode-select\");\n const minReservedCashInput = el(\"min-reserved-cash-input\");\n const reservedCashRatioInput = el(\"reserved-cash-ratio-input\");\n const showPrivateControls = hasPrivateConfig();\n\n el(\"switch-panel\").style.setProperty(\"--platform-color\", meta.accent);\n el(\"platform-title\").textContent = meta.label;\n el(\"quick-form\").hidden = !showPrivateControls;\n el(\"run-area\").hidden = !showPrivateControls;\n el(\"public-note\").hidden = showPrivateControls;\n el(\"public-preview\").hidden = showPrivateControls;\n el(\"public-note\").textContent = state.auth.allowed ? t(\"missingConfigNote\") : t(\"publicReadonly\");\n\n if (!showPrivateControls) {\n accountSelect.replaceChildren();\n strategySelect.replaceChildren();\n runtimeTargetEnabledSelect.replaceChildren();\n pluginModeSelect.replaceChildren();\n incomeLayerModeSelect.replaceChildren();\n optionOverlayModeSelect.replaceChildren();\n cashOnlyExecutionModeSelect.replaceChildren();\n dcaModeSelect.replaceChildren();\n reservePolicyModeSelect.replaceChildren();\n incomeLayerStartUsdInput.value = \"\";\n incomeLayerMaxRatioInput.value = \"\";\n dcaBaseInvestmentUsdInput.value = \"\";\n minReservedCashInput.value = \"\";\n reservedCashRatioInput.value = \"\";\n el(\"account-meta\").textContent = \"\";\n el(\"strategy-meta\").textContent = \"\";\n el(\"income-layer-mode-meta\").textContent = \"\";\n el(\"income-layer-start-meta\").textContent = \"\";\n el(\"income-layer-ratio-meta\").textContent = \"\";\n el(\"option-overlay-mode-meta\").textContent = \"\";\n el(\"cash-only-execution-mode-meta\").textContent = \"\";\n el(\"dca-mode-meta\").textContent = \"\";\n el(\"dca-base-meta\").textContent = \"\";\n return;\n }\n\n accountSelect.replaceChildren();\n if (accounts.length) {\n for (const account of accounts) {\n accountSelect.append(new Option(account.label, account.key, false, account.key === form.accountKey));\n }\n } else {\n accountSelect.append(new Option(t(\"noAccount\"), \"\"));\n }\n el(\"account-meta\").textContent = accounts.length ? accountMetaText(platform) : \"\";\n\n if (choices.length && !choices.includes(form.strategy) && !currentStrategyBlocked) {\n form.strategy = choices[0];\n }\n strategySelect.disabled = !choices.length;\n strategySelect.replaceChildren();\n if (currentStrategyBlocked) {\n const blockedOption = new Option(\n strategyChoiceLabel(currentStrategy, platform, account, form.executionMode),\n currentStrategy,\n true,\n currentStrategy === form.strategy,\n );\n blockedOption.disabled = true;\n strategySelect.append(blockedOption);\n }\n if (choices.length) {\n for (const strategy of choices) {\n strategySelect.append(\n new Option(strategyChoiceLabel(strategy, platform, account, form.executionMode), strategy, false, strategy === form.strategy),\n );\n }\n } else {\n strategySelect.append(new Option(t(\"noStrategy\"), \"\"));\n }\n el(\"strategy-meta\").textContent = account\n ? strategyDisplayMetaText(platform, account, form.strategy)\n : \"\";\n runtimeTargetEnabledSelect.replaceChildren();\n for (const mode of runtimeTargetModes) {\n runtimeTargetEnabledSelect.append(\n new Option(runtimeTargetModeLabel(mode), mode, false, mode === normalizeRuntimeTargetMode(form.runtimeTargetMode)),\n );\n }\n pluginModeSelect.replaceChildren();\n for (const mode of pluginModes) {\n pluginModeSelect.append(new Option(pluginModeLabel(mode), mode, false, mode === normalizePluginMode(form.pluginMode)));\n }\n const incomeDefaults = incomeLayerDefaultForStrategy(form.strategy);\n el(\"income-layer-section\").hidden = false;\n el(\"option-overlay-section\").hidden = false;\n incomeLayerModeSelect.replaceChildren();\n if (incomeDefaults) {\n incomeLayerModeSelect.disabled = false;\n for (const mode of incomeLayerModes) {\n incomeLayerModeSelect.append(new Option(incomeLayerModeLabel(mode), mode, false, mode === normalizeIncomeLayerMode(form.incomeLayerMode)));\n }\n el(\"income-layer-mode-meta\").textContent = incomeLayerDefaultMetaText(incomeDefaults);\n el(\"income-layer-start-meta\").textContent = t(\"incomeLayerStartMeta\");\n el(\"income-layer-ratio-meta\").textContent = t(\"incomeLayerAllocationMeta\").replace(\n \"{allocations}\",\n incomeLayerAllocationText(incomeDefaults),\n );\n } else {\n incomeLayerModeSelect.disabled = true;\n incomeLayerModeSelect.append(new Option(t(\"incomeLayerNotSupported\"), \"current\"));\n el(\"income-layer-mode-meta\").textContent = t(\"incomeLayerModeMeta\");\n el(\"income-layer-start-meta\").textContent = t(\"incomeLayerStartMeta\");\n el(\"income-layer-ratio-meta\").textContent = t(\"incomeLayerRatioMeta\");\n }\n const supportsMargin = platformSupportsMarginPolicy(platform);\n const supportsReserve = platformSupportsReservedCashPolicy(platform);\n if (supportsMargin) syncCashOnlyExecutionForAccount(platform);\n reconcileExecutionCashPolicy(form, \"margin\");\n const executionCashPolicyGrid = el(\"execution-cash-policy-grid\");\n const qmtPlatformCashNote = el(\"qmt-platform-cash-note\");\n const executionCashPolicyNote = el(\"execution-cash-policy-note\");\n executionCashPolicyGrid.hidden = !supportsMargin && !supportsReserve;\n qmtPlatformCashNote.hidden = supportsMargin || supportsReserve || platform !== \"qmt\";\n executionCashPolicyNote.hidden = !supportsMargin || !supportsReserve;\n\n const marginBlocksReserve = supportsMargin && supportsReserve && allowMarginExplicitlySelected(form);\n const reserveBlocksMargin = supportsMargin && supportsReserve && reserveCashOverrideActive(form);\n\n if (supportsReserve) {\n reservePolicyModeSelect.replaceChildren();\n for (const mode of reservePolicyModes) {\n reservePolicyModeSelect.append(new Option(t(`reservePolicy${mode[0].toUpperCase()}${mode.slice(1)}`), mode, false, mode === normalizeReservePolicyMode(form.reservePolicyMode)));\n }\n const reserveMode = normalizeReservePolicyMode(form.reservePolicyMode);\n el(\"min-reserved-cash-label\").textContent = t(\"minReservedCash\").replace(\n \"{currency}\",\n selectedCashCurrency(platform, account),\n );\n reservePolicyModeSelect.disabled = false;\n minReservedCashInput.disabled = reserveMode === \"current\" || reserveMode === \"none\" || reserveMode === \"ratio\";\n reservedCashRatioInput.disabled = reserveMode === \"current\" || reserveMode === \"none\" || reserveMode === \"floor\";\n minReservedCashInput.value = reserveMode === \"ratio\" || reserveMode === \"none\" ? \"\" : form.minReservedCashUsd;\n reservedCashRatioInput.value = reserveMode === \"floor\" || reserveMode === \"none\" ? \"\" : form.reservedCashRatio;\n el(\"reserve-policy-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"min-reserve-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"reserve-ratio-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"reserve-policy-mode-meta\").textContent = marginBlocksReserve\n ? t(\"executionCashMarginBlocksReserve\")\n : t(\"reservedCashModeMeta\");\n } else {\n reservePolicyModeSelect.replaceChildren();\n minReservedCashInput.value = \"\";\n reservedCashRatioInput.value = \"\";\n }\n const incomeMode = normalizeIncomeLayerMode(form.incomeLayerMode);\n const incomeLayerInputsDisabled = !incomeDefaults || incomeMode === \"disabled\";\n incomeLayerStartUsdInput.disabled = incomeLayerInputsDisabled;\n incomeLayerMaxRatioInput.disabled = incomeLayerInputsDisabled;\n if (incomeDefaults && incomeMode !== \"disabled\" && !cleanDisplayNumber(form.incomeLayerStartUsd)) {\n form.incomeLayerStartUsd = String(incomeDefaults.startUsd);\n }\n if (incomeDefaults && incomeMode !== \"disabled\" && !cleanDisplayRatio(form.incomeLayerMaxRatio)) {\n form.incomeLayerMaxRatio = incomeDefaults.maxRatio;\n }\n incomeLayerStartUsdInput.value = incomeDefaults && incomeMode !== \"disabled\" ? form.incomeLayerStartUsd : \"\";\n incomeLayerMaxRatioInput.value = incomeDefaults && incomeMode !== \"disabled\" ? form.incomeLayerMaxRatio : \"\";\n\n const optionDefaults = optionOverlayDefaultForStrategy(form.strategy);\n optionOverlayModeSelect.replaceChildren();\n if (optionDefaults) {\n optionOverlayModeSelect.disabled = false;\n for (const mode of optionOverlayModes) {\n optionOverlayModeSelect.append(\n new Option(optionOverlayModeLabel(mode), mode, false, mode === normalizeOptionOverlayMode(form.optionOverlayMode)),\n );\n }\n el(\"option-overlay-mode-meta\").textContent = optionOverlayDefaultMetaText(optionDefaults);\n } else {\n optionOverlayModeSelect.disabled = true;\n optionOverlayModeSelect.append(new Option(t(\"optionOverlayNotSupported\"), \"current\"));\n el(\"option-overlay-mode-meta\").textContent = t(\"optionOverlayModeMeta\");\n }\n\n if (supportsMargin) {\n cashOnlyExecutionModeSelect.replaceChildren();\n for (const mode of cashOnlyExecutionModes) {\n const option = new Option(\n mode === \"enabled\" ? t(\"cashOnlyExecutionNo\") : t(\"cashOnlyExecutionYes\"),\n mode,\n false,\n mode === normalizeCashOnlyExecutionMode(form.cashOnlyExecutionMode),\n );\n cashOnlyExecutionModeSelect.append(option);\n }\n el(\"cash-only-policy-block\").classList.toggle(\"policy-block-muted\", reserveBlocksMargin);\n el(\"cash-only-execution-mode-meta\").textContent = reserveBlocksMargin\n ? t(\"executionCashReserveBlocksMargin\")\n : t(\"cashOnlyExecutionModeMeta\");\n } else {\n cashOnlyExecutionModeSelect.replaceChildren();\n el(\"cash-only-execution-mode-meta\").textContent = \"\";\n }\n\n const dcaDefaults = dcaConfigForStrategy(form.strategy);\n dcaModeSelect.replaceChildren();\n const dcaAllowed = Boolean(dcaDefaults) && platformSupportsDca(platform);\n if (dcaAllowed) {\n dcaModeSelect.disabled = false;\n for (const mode of dcaModes) {\n dcaModeSelect.append(new Option(dcaModeLabel(mode), mode, false, mode === normalizeDcaMode(form.dcaMode)));\n }\n if (!cleanDisplayPositiveNumber(form.dcaBaseInvestmentUsd)) {\n form.dcaBaseInvestmentUsd = dcaDefaults.defaultBaseInvestmentUsd;\n }\n dcaBaseInvestmentUsdInput.disabled = false;\n dcaBaseInvestmentUsdInput.value = form.dcaBaseInvestmentUsd;\n el(\"dca-mode-meta\").textContent = t(\"dcaDefaultMeta\")\n .replace(\"{mode}\", dcaModeLabel(dcaDefaults.defaultMode))\n .replace(\"{amount}\", formatUsd(dcaDefaults.defaultBaseInvestmentUsd));\n el(\"dca-base-meta\").textContent = t(\"dcaModeMeta\");\n } else {\n dcaModeSelect.disabled = true;\n dcaModeSelect.append(new Option(\n dcaDefaults && !platformSupportsDca(platform) ? t(\"dcaPlatformNotSupported\") : t(\"dcaNotSupported\"),\n \"fixed\",\n ));\n dcaBaseInvestmentUsdInput.disabled = true;\n dcaBaseInvestmentUsdInput.value = \"\";\n el(\"dca-mode-meta\").textContent = t(\"dcaModeMeta\");\n el(\"dca-base-meta\").textContent = t(\"dcaModeMeta\");\n }\n\n const supportedModes = supportedExecutionModesForPlatform(platform);\n const liveModeAvailable = supportedModes.includes(\"live\") && hasLiveStrategyOption(platform, account);\n if (!supportedModes.includes(form.executionMode)) form.executionMode = \"dry_run\";\n document.querySelectorAll(\"#mode-control [data-mode]\").forEach((button) => {\n button.disabled = !supportedModes.includes(button.dataset.mode) || (\n button.dataset.mode === \"live\" && !liveModeAvailable\n );\n button.classList.toggle(\"active\", button.dataset.mode === form.executionMode);\n });\n el(\"mode-meta\").textContent = !supportedModes.includes(\"live\")\n ? t(\"qmtDryRunOnlyNote\")\n : (!liveModeAvailable ? t(\"liveModeUnavailable\") : \"\");\n }\n\n function renderSummary() {\n const showSummary = hasPrivateConfig();\n const summaryPanel = document.querySelector(\".summary-panel\");\n const switchSurface = document.querySelector(\".switch-surface\");\n summaryPanel.hidden = !showSummary;\n switchSurface.classList.toggle(\"summary-hidden\", !showSummary);\n if (!showSummary) return;\n\n const inputs = buildInputs();\n const list = el(\"summary-list\");\n list.replaceChildren();\n document.querySelector(\".summary-head h2\").textContent = t(\"summary\");\n const currentHeading = document.createElement(\"p\");\n currentHeading.className = \"summary-section-title\";\n currentHeading.textContent = t(\"summaryCurrent\");\n list.appendChild(currentHeading);\n let pendingSectionInserted = false;\n for (const [label, value, rowClass, valueTone] of summaryRows(inputs)) {\n if (!pendingSectionInserted && rowClass === \"pending\") {\n const pendingHeading = document.createElement(\"p\");\n pendingHeading.className = \"summary-section-title\";\n pendingHeading.textContent = t(\"summaryPending\");\n list.appendChild(pendingHeading);\n pendingSectionInserted = true;\n }\n const row = document.createElement(\"div\");\n row.className = \"summary-row\";\n row.setAttribute(\"role\", \"listitem\");\n if (rowClass) row.classList.add(rowClass);\n const labelNode = document.createElement(\"div\");\n labelNode.className = \"summary-label\";\n labelNode.textContent = label;\n const valueNode = document.createElement(\"div\");\n valueNode.className = \"summary-value\";\n if (valueTone) {\n const badge = document.createElement(\"span\");\n badge.className = `summary-status ${valueTone}`;\n badge.textContent = value;\n valueNode.appendChild(badge);\n } else {\n valueNode.textContent = value;\n }\n row.append(labelNode, valueNode);\n list.appendChild(row);\n }\n\n const account = selectedAccount();\n const currentEntry = currentEntryForAccount(state.selected, account);\n const currentMode = normalizeExecutionMode(currentEntry?.execution_mode, currentEntry?.dry_run_only);\n el(\"mode-pill\").textContent = currentMode ? modeLabel(currentMode) : t(\"notRead\");\n }\n\n function setPlanCheck(id, value, tone) {\n const node = el(id);\n if (!node) return;\n node.textContent = value;\n node.dataset.tone = tone || \"neutral\";\n }\n\n function renderPlanReadiness() {\n const platform = state.selected;\n const account = selectedAccount(platform);\n const configured = hasPrivateConfig() && Boolean(account?.key);\n const runnable = configured && hasRunnableStrategySelection(platform);\n const riskValid = configured && hasValidExecutionCashPolicy(platform) &&\n hasValidIncomeLayerPolicy(platform) && hasValidOptionOverlayPolicy(platform) && hasValidDcaPolicy(platform);\n const form = state.forms[platform];\n const isNonLive = normalizeExecutionMode(form?.executionMode, false) !== \"live\";\n setPlanCheck(\n \"plan-check-account\",\n configured ? t(\"planCheckSelected\") : t(\"planCheckWaiting\"),\n configured ? \"ready\" : \"neutral\",\n );\n setPlanCheck(\n \"plan-check-strategy\",\n runnable ? t(\"planCheckValid\") : (configured ? t(\"planCheckFix\") : t(\"planCheckWaiting\")),\n runnable ? \"ready\" : (configured ? \"warning\" : \"neutral\"),\n );\n setPlanCheck(\n \"plan-check-risk\",\n riskValid ? t(\"planCheckValid\") : (configured ? t(\"planCheckFix\") : t(\"planCheckWaiting\")),\n riskValid ? \"ready\" : (configured ? \"warning\" : \"neutral\"),\n );\n setPlanCheck(\n \"plan-check-authority\",\n isNonLive ? t(\"planCheckNonLive\") : t(\"planCheckNoAuthority\"),\n isNonLive ? \"ready\" : \"warning\",\n );\n }\n\n function renderAuth() {\n const status = el(\"auth-status\");\n const loginLink = el(\"login-link\");\n const logoutButton = el(\"logout-button\");\n const signedIn = Boolean(state.auth.allowed && state.auth.login);\n\n status.hidden = !signedIn;\n status.textContent = signedIn ? t(\"signedInAs\").replace(\"{login}\", state.auth.login) : \"\";\n loginLink.hidden = signedIn;\n loginLink.href = \"/login\";\n loginLink.textContent = t(\"login\");\n logoutButton.hidden = !signedIn;\n logoutButton.textContent = t(\"logout\");\n\n const dispatch = el(\"dispatch-button\");\n const hasPrivateAccounts = state.configSource === \"private\";\n const loadingConfig = state.configSource === \"loading\";\n const hasRunnableStrategy = hasRunnableStrategySelection();\n const hasValidReserve = hasValidExecutionCashPolicy();\n const hasValidIncomeLayer = hasValidIncomeLayerPolicy();\n const hasValidOptionOverlay = hasValidOptionOverlayPolicy();\n const hasValidDca = hasValidDcaPolicy();\n const hasValidStrategy = hasRunnableStrategy &&\n hasValidReserve &&\n hasValidIncomeLayer &&\n hasValidOptionOverlay &&\n hasValidDca;\n const hasPendingChange = hasPrivateAccounts && hasValidStrategy && hasPendingChanges(buildInputs());\n dispatch.disabled = !state.auth.allowed || loadingConfig || !hasPrivateAccounts || !hasValidStrategy || !hasPendingChange;\n dispatch.textContent = state.auth.allowed\n ? (loadingConfig\n ? t(\"loadingConfig\")\n : (hasPrivateAccounts ? (hasValidStrategy ? (hasPendingChange ? t(\"runSwitch\") : t(\"noChanges\")) : t(\"configureAccounts\")) : t(\"configureAccounts\")))\n : t(\"loginToRun\");\n const note = el(\"action-note\");\n note.textContent = state.auth.allowed\n ? (loadingConfig\n ? t(\"loadingConfigNote\")\n : (hasPrivateAccounts\n ? (hasRunnableStrategy\n ? (hasValidReserve\n ? (hasValidIncomeLayer\n ? (hasValidOptionOverlay\n ? (hasValidDca ? (hasPendingChange ? t(\"readyNote\") : \"\") : t(\"invalidDcaNote\"))\n : t(\"invalidOptionOverlayNote\"))\n : t(\"invalidIncomeLayerNote\"))\n : (executionCashPolicyConflict(state.forms[state.selected])\n ? t(\"invalidExecutionCashPolicyNote\")\n : t(\"invalidReservePolicyNote\")))\n : strategyActionNoteText())\n : t(\"missingConfigNote\")))\n : t(\"readonlyNote\");\n note.classList.toggle(\n \"warning\",\n state.auth.allowed && !loadingConfig && (!hasPrivateAccounts || !hasValidStrategy),\n );\n }\n\n function renderAppVisibility() {\n document.body.classList.toggle(\"app-loading\", !state.appReady);\n el(\"boot-message\").textContent = t(state.bootMessageKey);\n }\n\n function normalizeControlPlanePayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid control plane payload\");\n const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n attention: payload.attention && typeof payload.attention === \"object\" && !Array.isArray(payload.attention)\n ? {\n status: [\"research_only\", \"attention_required\", \"unavailable\"].includes(payload.attention.status)\n ? payload.attention.status\n : \"unavailable\",\n reason_codes: Array.isArray(payload.attention.reason_codes) ? payload.attention.reason_codes : [],\n }\n : { status: \"unavailable\", reason_codes: [\"control_plane_attention_missing\"] },\n candidates: candidates.filter((item) => item && typeof item === \"object\" && item.lifecycle && typeof item.lifecycle === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function normalizeOwnerDecisionQueue(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid owner decision queue\");\n const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n candidates: candidates.filter((entry) => entry && typeof entry === \"object\"\n && entry.candidate && typeof entry.candidate === \"object\"\n && typeof entry.candidate_evidence_sha256 === \"string\"),\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function ownerDecisionEntry(candidateId) {\n return state.ownerDecisions.candidates.find((entry) => entry.candidate?.candidate_id === candidateId) || null;\n }\n\n function ownerDecisionLabel(decision) {\n return {\n approve_limited_live_canary: t(\"ownerDecisionApprove\"),\n keep_parked: t(\"ownerDecisionPark\"),\n retire_candidate: t(\"ownerDecisionRetire\"),\n }[decision] || String(decision || \"—\");\n }\n\n function controlPlaneDataStatusText(status) {\n return status === \"ready\"\n ? t(\"controlDataReady\")\n : (status === \"stale\" ? t(\"controlDataStale\") : t(\"controlDataUnavailable\"));\n }\n\n function controlPlaneAttentionText(attention) {\n const status = attention?.status || \"unavailable\";\n if (status === \"research_only\") return t(\"controlAttentionResearchOnly\");\n if (status === \"attention_required\") return t(\"controlAttentionRequired\");\n return t(\"controlAttentionUnavailable\");\n }\n\n function renderControlPlane() {\n const payload = state.controlPlane.payload;\n const summary = payload.summary || {};\n el(\"control-plane-status\").textContent = `${controlPlaneDataStatusText(payload.data_status)} · ${controlPlaneAttentionText(payload.attention)}`;\n el(\"control-plane-computed-at\").textContent = payload.computed_at\n ? t(\"controlComputedAt\").replace(\"{time}\", new Date(payload.computed_at).toLocaleString())\n : t(\"controlComputedAt\").replace(\"{time}\", \"—\");\n el(\"control-count-candidates\").textContent = String(Number(summary.candidate_count) || 0);\n el(\"control-count-deferred\").textContent = String(Number(summary.deferred) || 0);\n el(\"control-count-parked\").textContent = String(Number(summary.parked) || 0);\n el(\"control-count-owner-decision\").textContent = String(Number(summary.owner_decision_required) || 0);\n\n const notice = el(\"control-plane-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"controlLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"controlStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"controlUnavailableNotice\");\n } else if (payload.attention?.status === \"attention_required\") {\n notice.textContent = t(\"controlAttentionNotice\")\n .replace(\"{deferred}\", String(Number(summary.deferred) || 0))\n .replace(\"{parked}\", String(Number(summary.parked) || 0))\n .replace(\"{signals}\", String(payload.attention.reason_codes?.length || 0));\n } else if (payload.errors?.length) {\n notice.textContent = t(\"controlUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || \"live 仍需所有者明确决定。\";\n }\n\n const list = el(\"control-plane-list\");\n list.replaceChildren();\n if (!payload.candidates.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"controlEmptyCandidates\");\n list.appendChild(empty);\n return;\n }\n for (const item of payload.candidates) {\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = `${item.candidate_kind || \"candidate\"} · ${domainLabel(item.domain || \"\")}`;\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(item.candidate_id || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = item.recommendation?.reason || t(\"controlNoRecommendation\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"controlStageMeta\")\n .replace(\"{stage}\", item.lifecycle?.stage || \"—\")\n .replace(\"{status}\", item.lifecycle?.status || \"unknown\")\n .replace(\"{freshness}\", item.freshness?.status || \"unknown\");\n main.append(meta, title, reason, detail);\n const stateBlock = document.createElement(\"div\");\n stateBlock.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = t(\"controlNext\");\n const stage = document.createElement(\"strong\");\n stage.textContent = item.lifecycle?.stage || \"—\";\n const recommendation = document.createElement(\"small\");\n recommendation.textContent = item.recommendation?.code || \"none\";\n stateBlock.append(label, stage, recommendation);\n const ownerEntry = ownerDecisionEntry(item.candidate_id);\n if (ownerEntry) {\n const ownerDecision = document.createElement(\"div\");\n ownerDecision.className = \"owner-decision\";\n const ownerTitle = document.createElement(\"strong\");\n ownerTitle.textContent = t(\"ownerDecisionTitle\");\n const ownerDetail = document.createElement(\"small\");\n if (ownerEntry.intent) {\n ownerDetail.textContent = t(\"ownerDecisionRecorded\")\n .replace(\"{decision}\", ownerDecisionLabel(ownerEntry.intent.decision));\n } else if (!state.auth.admin) {\n ownerDetail.textContent = t(\"ownerDecisionAdminOnly\");\n } else {\n ownerDetail.textContent = t(\"ownerDecisionReady\");\n }\n ownerDecision.append(ownerTitle, ownerDetail);\n if (!ownerEntry.intent && state.auth.admin) {\n const actions = document.createElement(\"div\");\n actions.className = \"owner-decision__actions\";\n const submitting = state.ownerDecisions.submittingCandidateId === item.candidate_id;\n for (const [decision, labelKey] of [\n [\"approve_limited_live_canary\", \"ownerDecisionApprove\"],\n [\"keep_parked\", \"ownerDecisionPark\"],\n [\"retire_candidate\", \"ownerDecisionRetire\"],\n ]) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"owner-decision__button\";\n button.dataset.ownerDecision = decision;\n button.dataset.candidateId = item.candidate_id;\n button.dataset.candidateEvidenceSha256 = ownerEntry.candidate_evidence_sha256;\n button.disabled = submitting;\n button.textContent = submitting ? t(\"ownerDecisionSubmitting\") : t(labelKey);\n actions.appendChild(button);\n }\n ownerDecision.appendChild(actions);\n }\n main.appendChild(ownerDecision);\n }\n card.append(main, stateBlock);\n list.appendChild(card);\n }\n }\n\n function normalizeAdaptiveSelectionPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid adaptive selection payload\");\n const selections = Array.isArray(payload.selections) ? payload.selections : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n selections: selections.filter((item) => item && typeof item === \"object\"\n && typeof item.source_id === \"string\" && item.decision && typeof item.decision === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderAdaptiveSelection() {\n const payload = state.adaptiveSelection.payload;\n const notice = el(\"adaptive-selection-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"adaptiveSelectionLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"adaptiveSelectionStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"adaptiveSelectionUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"adaptiveSelectionUpstreamNotice\").replace(\"{count}\", String(payload.errors.length));\n } else {\n notice.textContent = payload.policy?.notice || t(\"adaptiveSelectionNoOrder\");\n }\n\n const list = el(\"adaptive-selection-list\");\n list.replaceChildren();\n if (!payload.selections.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"adaptiveSelectionEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.selections) {\n const decision = entry.decision || {};\n const context = decision.market_context || {};\n const recommended = decision.candidates?.find((item) => item.accepted\n && item.strategy_profile === decision.recommended_strategy_profile) || null;\n const rejected = (decision.candidates || []).filter((item) => !item.accepted);\n const reasons = (recommended?.reasons?.length ? recommended.reasons : rejected.flatMap((item) => item.reasons || []))\n .slice(0, 3)\n .join(\", \");\n\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"adaptiveSelectionMeta\")\n .replace(\"{source}\", entry.source_id || \"unknown\")\n .replace(\"{domain}\", domainLabel(context.domain || \"\"))\n .replace(\"{asOf}\", context.as_of || \"—\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = recommended\n ? `${t(\"adaptiveSelectionRecommended\")} · ${recommended.strategy_profile}`\n : t(\"adaptiveSelectionNoCandidate\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = reasons\n ? t(\"adaptiveSelectionReason\").replace(\"{reasons}\", reasons)\n : t(\"adaptiveSelectionNoOrder\");\n main.append(meta, title, reason);\n const status = document.createElement(\"div\");\n status.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = \"NO ORDER\";\n const score = document.createElement(\"strong\");\n score.textContent = typeof recommended?.score === \"number\" ? recommended.score.toFixed(3) : \"—\";\n const freshness = document.createElement(\"small\");\n freshness.textContent = entry.freshness?.data_status || \"unknown\";\n status.append(label, score, freshness);\n card.append(main, status);\n list.appendChild(card);\n }\n }\n\n function normalizeExecutionEvidencePayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid execution evidence payload\");\n const deployments = Array.isArray(payload.deployments) ? payload.deployments : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n deployments: deployments.filter((item) => item && typeof item === \"object\" && item.deployment && typeof item.deployment === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderExecutionEvidence() {\n const payload = state.executionEvidence.payload;\n const notice = el(\"execution-evidence-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"executionEvidenceLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"executionEvidenceStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"executionEvidenceUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"executionEvidenceUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || t(\"executionEvidenceNoOrder\");\n }\n\n const list = el(\"execution-evidence-list\");\n list.replaceChildren();\n if (!payload.deployments.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"executionEvidenceEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.deployments) {\n const deployment = entry.deployment || {};\n const strategy = deployment.strategy || {};\n const target = deployment.target || {};\n const capabilities = deployment.capabilities || {};\n const evidence = deployment.evidence || {};\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"executionEvidenceMeta\")\n .replace(\"{platform}\", target.platform || \"unknown\")\n .replace(\"{environment}\", target.environment || \"unknown\")\n .replace(\"{source}\", entry.source_id || \"unknown\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(strategy.candidate_id || deployment.deployment_id || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = deployment.recommendation?.reason_code || t(\"executionEvidenceNoOrder\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"executionEvidenceDetail\")\n .replace(\"{strategy}\", evidence.strategy || \"unknown\")\n .replace(\"{data}\", evidence.target_data || \"unknown\")\n .replace(\"{execution}\", evidence.target_execution || \"unknown\")\n .replace(\"{shadow}\", capabilities.shadow || \"unknown\")\n .replace(\"{paper}\", capabilities.paper || \"unknown\");\n main.append(meta, title, reason, detail);\n const stateBlock = document.createElement(\"div\");\n stateBlock.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = t(\"executionEvidenceNext\");\n const recommendation = document.createElement(\"strong\");\n recommendation.textContent = deployment.recommendation?.code || \"parked\";\n const freshness = document.createElement(\"small\");\n freshness.textContent = entry.freshness?.data_status || \"unknown\";\n stateBlock.append(label, recommendation, freshness);\n card.append(main, stateBlock);\n list.appendChild(card);\n }\n }\n\n function normalizeResearchTaskPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid research task payload\");\n const tasks = Array.isArray(payload.tasks) ? payload.tasks : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n tasks: tasks.filter((item) => item && typeof item === \"object\" && item.task && typeof item.task === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function shortResearchDigest(value) {\n const text = String(value || \"\");\n return /^[0-9a-f]{64}$/.test(text) ? `${text.slice(0, 10)}…` : \"—\";\n }\n\n function renderResearchTasks() {\n const payload = state.researchTasks.payload;\n const notice = el(\"research-task-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"researchTaskLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"researchTaskStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"researchTaskUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"researchTaskUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || t(\"researchTaskNoOrder\");\n }\n\n const list = el(\"research-task-list\");\n list.replaceChildren();\n if (!payload.tasks.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"researchTaskEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.tasks) {\n const task = entry.task || {};\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"researchTaskMeta\")\n .replace(\"{type}\", task.task_type || \"research\")\n .replace(\"{domain}\", domainLabel(task.target?.domain || \"\"))\n .replace(\"{created}\", task.created_at ? new Date(task.created_at).toLocaleString() : \"—\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = `${task.target?.candidate_id || \"unknown\"} · ${task.task_id || \"unknown\"}`;\n const hypothesis = document.createElement(\"p\");\n hypothesis.className = \"health-card__reason\";\n hypothesis.textContent = task.experiment?.hypothesis || t(\"researchTaskNoOrder\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"researchTaskLimits\")\n .replace(\"{runs}\", String(task.experiment?.max_runs || \"—\"))\n .replace(\"{seconds}\", String(task.experiment?.max_wall_seconds || \"—\"))\n .replace(\"{p1}\", shortResearchDigest(task.evidence?.p1_input_digest))\n .replace(\"{p2}\", shortResearchDigest(task.evidence?.p2_config_digest))\n .replace(\"{p3}\", shortResearchDigest(task.evidence?.p3_evidence_id));\n main.append(meta, title, hypothesis, detail);\n const status = document.createElement(\"div\");\n status.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = \"NO ORDER\";\n const count = document.createElement(\"strong\");\n count.textContent = String(task.experiment?.max_runs || \"—\");\n const source = document.createElement(\"small\");\n source.textContent = entry.freshness?.data_status || \"unknown\";\n status.append(label, count, source);\n card.append(main, status);\n list.appendChild(card);\n }\n }\n\n function healthStatusLabel(status) {\n return { healthy: \"健康\", watch: \"观察\", review: \"复核\", critical: \"严重\" }[status] || \"未知\";\n }\n\n function normalizeHealthPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid health payload\");\n const strategies = Array.isArray(payload.strategies) ? payload.strategies : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n strategies: strategies.filter((item) => item && typeof item === \"object\" && [\"healthy\", \"watch\", \"review\", \"critical\"].includes(item.status)),\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderHealth() {\n const payload = state.health.payload;\n const summary = payload.summary || {};\n const statusText = payload.data_status === \"ready\"\n ? \"快照已加载\"\n : (payload.data_status === \"stale\" ? \"快照已过期\" : \"等待可用快照\");\n el(\"health-status\").textContent = statusText;\n el(\"health-computed-at\").textContent = payload.computed_at\n ? `最近计算:${new Date(payload.computed_at).toLocaleString()}`\n : \"最近计算:—\";\n el(\"health-count-total\").textContent = String(Number(summary.strategy_count) || 0);\n el(\"health-count-healthy\").textContent = String(Number(summary.healthy) || 0);\n el(\"health-count-watch\").textContent = String(Number(summary.watch) || 0);\n el(\"health-count-review\").textContent = String(Number(summary.review) || 0);\n el(\"health-count-critical\").textContent = String(Number(summary.critical) || 0);\n\n const notice = el(\"health-notice\");\n if (!state.auth.allowed) {\n notice.textContent = \"登录后读取私有策略健康快照;没有快照时不会展示虚构指标。\";\n } else if (payload.data_status === \"stale\") {\n notice.textContent = \"健康快照已超过允许的新鲜度窗口;页面保留原始状态,但不会把它当作当前健康结论。\";\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = \"还没有可用的策略健康快照;当前页面保持 fail-closed 空状态。\";\n } else if (payload.errors?.length) {\n notice.textContent = `快照已加载,但有 ${payload.errors.length} 个上游提示;缺失数据不会被替换成虚构指标。`;\n } else {\n notice.textContent = \"健康不等于已批准 live;正常实盘、资金和杠杆变更仍需人工确认。\";\n }\n\n const list = el(\"health-list\");\n list.replaceChildren();\n const strategies = payload.strategies.filter((item) => state.health.filter === \"all\" || item.status === state.health.filter);\n if (!strategies.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = \"暂无可展示的策略健康快照。\";\n list.appendChild(empty);\n return;\n }\n for (const item of strategies) {\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = `${healthStatusLabel(item.status)} · ${item.domain || \"unknown\"}`;\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(item.profile || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = `${item.decision?.label || \"证据不足,保持研究态\"}。${item.decision?.reason || \"没有可用的机器检查结果。\"}`;\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = `阶段:${item.review?.requested_stage || \"未标记\"} · 截至:${item.as_of || \"—\"}`;\n main.append(meta, title, reason, detail);\n const scoreBlock = document.createElement(\"div\");\n scoreBlock.className = \"health-card__score\";\n const scoreLabel = document.createElement(\"small\");\n scoreLabel.textContent = \"HEALTH\";\n const score = document.createElement(\"strong\");\n score.textContent = typeof item.score === \"number\" ? item.score.toFixed(1) : \"—\";\n const decision = document.createElement(\"small\");\n decision.textContent = item.decision?.code || \"evidence_missing\";\n scoreBlock.append(scoreLabel, score, decision);\n card.append(main, scoreBlock);\n list.appendChild(card);\n }\n }\n\n function renderConsoleView() {\n const controlButton = el(\"control-plane-view-button\");\n const healthButton = el(\"health-view-button\");\n const switchButton = el(\"switch-view-button\");\n const controlVisible = state.view === \"control\";\n const healthVisible = state.view === \"health\";\n el(\"control-plane-view\").hidden = !controlVisible;\n el(\"health-view\").hidden = !healthVisible;\n el(\"switch-view\").hidden = controlVisible || healthVisible;\n controlButton.classList.toggle(\"active\", controlVisible);\n healthButton.classList.toggle(\"active\", healthVisible);\n switchButton.classList.toggle(\"active\", !controlVisible && !healthVisible);\n }\n\n function render() {\n applyLanguage();\n renderRuntimeAuthorityStatus();\n renderConsoleView();\n renderControlPlane();\n renderAdaptiveSelection();\n renderExecutionEvidence();\n renderResearchTasks();\n renderHealth();\n renderPlatforms();\n renderControls();\n renderSummary();\n renderPlanReadiness();\n renderAuth();\n renderAppVisibility();\n }\n\n async function refreshSession() {\n state.bootMessageKey = \"bootSession\";\n render();\n try {\n const session = await requestJson(\"/api/session\");\n state.auth = {\n available: true,\n allowed: Boolean(session.allowed),\n admin: Boolean(session.admin),\n login: session.login || null,\n };\n } catch {\n state.auth = { available: false, allowed: false, admin: false, login: null };\n }\n if (state.auth.allowed) {\n await refreshControlPlane();\n await refreshOwnerDecisions();\n await refreshAdaptiveSelection();\n await refreshExecutionEvidence();\n await refreshResearchTasks();\n await refreshHealth();\n await refreshConfig();\n } else {\n state.bootMessageKey = \"bootPublic\";\n state.appReady = true;\n render();\n }\n }\n\n async function refreshHealth() {\n if (!state.auth.allowed) {\n renderHealth();\n return;\n }\n try {\n state.health.payload = normalizeHealthPayload(await requestJson(\"/api/strategy-health\"));\n } catch {\n state.health.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { strategy_count: 0, healthy: 0, watch: 0, review: 0, critical: 0 },\n strategies: [],\n errors: [\"health_request_failed\"],\n };\n }\n renderHealth();\n }\n\n async function refreshControlPlane() {\n if (!state.auth.allowed) {\n renderControlPlane();\n return;\n }\n try {\n state.controlPlane.payload = normalizeControlPlanePayload(await requestJson(\"/api/control-plane\"));\n } catch {\n state.controlPlane.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { candidate_count: 0, deferred: 0, parked: 0, owner_decision_required: 0 },\n attention: { status: \"unavailable\", reason_codes: [\"control_plane_request_failed\"] },\n candidates: [],\n policy: { p4_p5_automation: \"not_configured\", p6_owner_decision_required: true },\n errors: [\"control_plane_request_failed\"],\n };\n }\n renderControlPlane();\n }\n\n async function refreshOwnerDecisions() {\n if (!state.auth.allowed) {\n state.ownerDecisions = { data_status: \"unavailable\", candidates: [], errors: [] };\n renderControlPlane();\n return;\n }\n try {\n state.ownerDecisions = normalizeOwnerDecisionQueue(await requestJson(\"/api/owner-decisions\"));\n } catch {\n state.ownerDecisions = {\n data_status: \"unavailable\",\n candidates: [],\n errors: [\"owner_decision_queue_request_failed\"],\n };\n }\n renderControlPlane();\n }\n\n async function refreshAdaptiveSelection() {\n if (!state.auth.allowed) {\n renderAdaptiveSelection();\n return;\n }\n try {\n state.adaptiveSelection.payload = normalizeAdaptiveSelectionPayload(await requestJson(\"/api/adaptive-selection\"));\n } catch {\n state.adaptiveSelection.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { source_count: 0, decision_count: 0, candidate_count: 0, recommended_count: 0, rejected_candidate_count: 0 },\n selections: [],\n policy: { authority: \"shadow_only\", no_order: true, execution_authority_granted: false },\n errors: [\"adaptive_selection_request_failed\"],\n };\n }\n renderAdaptiveSelection();\n }\n\n async function recordOwnerDecision(button) {\n if (!state.auth.admin) return;\n const candidateId = String(button.dataset.candidateId || \"\");\n const candidateEvidenceSha256 = String(button.dataset.candidateEvidenceSha256 || \"\");\n const decision = String(button.dataset.ownerDecision || \"\");\n if (!candidateId || !candidateEvidenceSha256 || !decision) return;\n if (!window.confirm(t(\"ownerDecisionConfirm\"))) return;\n state.ownerDecisions.submittingCandidateId = candidateId;\n renderControlPlane();\n try {\n const response = await fetch(\"/api/owner-decisions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n candidate_id: candidateId,\n decision,\n candidate_evidence_sha256: candidateEvidenceSha256,\n }),\n });\n const payload = await response.json();\n if (!response.ok || !payload.ok) throw new Error(payload.error || t(\"ownerDecisionFailed\"));\n showToast(t(\"ownerDecisionSuccess\"), { duration: 7000 });\n await refreshControlPlane();\n await refreshOwnerDecisions();\n } catch (error) {\n showToast(`${t(\"ownerDecisionFailed\")}: ${error.message}`, { duration: 12000 });\n } finally {\n delete state.ownerDecisions.submittingCandidateId;\n renderControlPlane();\n }\n }\n\n async function refreshExecutionEvidence() {\n if (!state.auth.allowed) {\n renderExecutionEvidence();\n return;\n }\n try {\n state.executionEvidence.payload = normalizeExecutionEvidencePayload(await requestJson(\"/api/execution-evidence\"));\n } catch {\n state.executionEvidence.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { deployment_count: 0, autonomous_shadow: 0, autonomous_paper: 0, owner_canary_decision: 0, parked: 0 },\n deployments: [],\n policy: { execution_evidence_read_only: true, p6_owner_decision_required: true, limited_live_canary_active: false },\n errors: [\"execution_evidence_request_failed\"],\n };\n }\n renderExecutionEvidence();\n }\n\n async function refreshResearchTasks() {\n if (!state.auth.allowed) {\n renderResearchTasks();\n return;\n }\n try {\n state.researchTasks.payload = normalizeResearchTaskPayload(await requestJson(\"/api/research-tasks\"));\n } catch {\n state.researchTasks.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { task_count: 0 },\n tasks: [],\n policy: { research_only: true, no_order: true, size_zero_required: true, p4_p5_p6_authorized: false },\n errors: [\"research_task_request_failed\"],\n };\n }\n renderResearchTasks();\n }\n\n async function refreshStrategyProfiles() {\n state.bootMessageKey = \"bootStrategy\";\n render();\n try {\n const payload = await requestJson(\"/api/strategy-profiles\");\n applyStrategyProfiles(payload.strategyProfiles || []);\n if (payload.platformMeta) platformMeta = payload.platformMeta;\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n render();\n } catch {\n applyStrategyProfiles(defaultStrategyProfiles);\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n }\n }\n\n async function refreshConfig() {\n if (!state.auth.available || !state.auth.allowed) return;\n state.configSource = \"loading\";\n state.bootMessageKey = \"bootConfig\";\n render();\n try {\n const payload = await requestJson(\"/api/config\");\n if (payload.accountOptions) {\n applyStrategyProfiles(payload.strategyProfiles || defaultStrategyProfiles);\n state.accountOptions = normalizeAccountOptions(payload.accountOptions);\n if (payload.platformMeta) platformMeta = payload.platformMeta;\n state.repositories = normalizePlatformRepositories(payload.platformRepositories || {});\n state.currentStrategies = normalizeCurrentStrategies(payload.currentStrategies || {});\n state.configSource = \"private\";\n for (const platform of Object.keys(platformMeta)) {\n ensureAccountSelection(platform);\n syncStrategyForAccount(platform);\n }\n } else {\n state.configSource = \"default\";\n state.currentStrategies = {};\n }\n } catch (error) {\n state.configSource = \"default\";\n state.currentStrategies = {};\n if (isRequestTimeoutError(error)) {\n state.bootMessageKey = \"bootTimeout\";\n } else {\n state.bootMessageKey = \"bootPublic\";\n }\n } finally {\n state.appReady = true;\n render();\n }\n }\n\n function normalizeAccountOptions(raw) {\n const normalized = clone(defaultAccountOptions);\n for (const platform of Object.keys(platformMeta)) {\n if (!Array.isArray(raw[platform]) || !raw[platform].length) continue;\n normalized[platform] = raw[platform].map((item, index) => ({\n key: String(item.key || item.target_name || index),\n label: String(item.label || item.target_name || item.key || platform),\n target_name: String(item.target_name || item.key || \"\"),\n account_selector: item.account_selector ? String(item.account_selector) : \"\",\n deployment_selector: item.deployment_selector ? String(item.deployment_selector) : \"\",\n account_scope: item.account_scope ? String(item.account_scope) : \"\",\n service_name: item.service_name ? String(item.service_name) : \"\",\n cash_currency: item.cash_currency || item.market_currency || item.trading_currency\n ? String(item.cash_currency || item.market_currency || item.trading_currency).trim().toUpperCase()\n : \"\",\n supported_domains: normalizeSupportedDomains(platform, item),\n github_environment: item.github_environment ? String(item.github_environment) : \"\",\n variable_scope: item.variable_scope ? String(item.variable_scope) : \"\",\n plugin_mode: item.plugin_mode ? String(item.plugin_mode) : \"\",\n option_overlay_mode: item.option_overlay_mode ? normalizeOptionOverlayMode(item.option_overlay_mode) : \"\",\n cash_only_execution_mode: item.cash_only_execution_mode\n ? normalizeCashOnlyExecutionMode(item.cash_only_execution_mode)\n : \"\",\n dca_mode: item.dca_mode ? normalizeDcaMode(item.dca_mode) : \"\",\n dca_base_investment_usd: cleanDisplayPositiveNumber(item.dca_base_investment_usd),\n }));\n }\n return normalized;\n }\n\n function normalizeSupportedDomains(platform, item) {\n const raw = Array.isArray(item?.supported_domains)\n ? item.supported_domains\n : String(item?.supported_domains || \"\").split(/[\\s,;]+/);\n const cleaned = raw.map(cleanStrategyDomain).filter(Boolean);\n if (cleaned.length) return [...new Set(cleaned)];\n return inferSupportedDomains(platform, item || {});\n }\n\n function normalizeCurrentStrategies(raw) {\n const normalized = {};\n for (const platform of Object.keys(platformMeta)) {\n if (!raw[platform] || typeof raw[platform] !== \"object\" || Array.isArray(raw[platform])) continue;\n normalized[platform] = {};\n for (const [key, entry] of Object.entries(raw[platform])) {\n const profile = cleanStrategyProfile(entry?.strategy_profile);\n const minReservedCashUsd = cleanDisplayNumber(entry?.min_reserved_cash_usd ?? entry?.reserved_cash_floor_usd);\n const reservedCashRatio = cleanDisplayRatio(entry?.reserved_cash_ratio);\n const incomeLayerEnabled = cleanOptionalBoolean(entry?.income_layer_enabled);\n const incomeLayerStartUsd = cleanDisplayNumber(entry?.income_layer_start_usd);\n const incomeLayerMaxRatio = cleanDisplayRatio(entry?.income_layer_max_ratio);\n const optionOverlayEnabled = cleanOptionalBoolean(entry?.option_overlay_enabled);\n const cashOnlyExecution = cleanOptionalBoolean(entry?.cash_only_execution);\n const runtimeTargetEnabled = cleanOptionalBoolean(entry?.runtime_target_enabled);\n const dcaMode = entry?.dca_mode ? normalizeDcaMode(entry.dca_mode) : \"\";\n const dcaBaseInvestmentUsd = cleanDisplayPositiveNumber(entry?.dca_base_investment_usd);\n const executionMode = normalizeExecutionMode(entry?.execution_mode, entry?.dry_run_only);\n if (\n !profile &&\n !minReservedCashUsd &&\n !reservedCashRatio &&\n incomeLayerEnabled === null &&\n !incomeLayerStartUsd &&\n !incomeLayerMaxRatio &&\n optionOverlayEnabled === null &&\n cashOnlyExecution === null &&\n runtimeTargetEnabled === null &&\n !dcaMode &&\n !dcaBaseInvestmentUsd &&\n !executionMode\n ) continue;\n normalized[platform][String(key)] = {\n strategy_profile: profile,\n execution_mode: executionMode,\n dry_run_only: entry?.dry_run_only === true || entry?.dry_run_only === \"true\" || entry?.dry_run_only === \"1\",\n min_reserved_cash_usd: minReservedCashUsd,\n reserved_cash_ratio: reservedCashRatio,\n income_layer_enabled: incomeLayerEnabled,\n income_layer_start_usd: incomeLayerStartUsd,\n income_layer_max_ratio: incomeLayerMaxRatio,\n option_overlay_enabled: optionOverlayEnabled,\n cash_only_execution: cashOnlyExecution,\n runtime_target_enabled: runtimeTargetEnabled,\n dca_mode: dcaMode,\n dca_base_investment_usd: dcaBaseInvestmentUsd,\n source: entry?.source ? String(entry.source) : \"\",\n };\n }\n if (!Object.keys(normalized[platform]).length) delete normalized[platform];\n }\n return normalized;\n }\n\n function normalizePlatformRepositories(raw) {\n const normalized = clone(defaultRepositories);\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return normalized;\n for (const platform of Object.keys(platformMeta)) {\n const repository = String(raw[platform] || \"\").trim();\n if (/^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/.test(repository)) {\n normalized[platform] = repository;\n }\n }\n return normalized;\n }\n\n async function dispatchSwitch() {\n if (!state.auth.allowed) return;\n showToast(t(\"dispatching\"), { duration: 0 });\n try {\n const response = await fetch(\"/api/switch\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(buildInputs()),\n });\n const payload = await response.json();\n if (!response.ok || !payload.ok) throw new Error(payload.error || t(\"dispatchFailed\"));\n showToast(t(\"dispatched\"), { duration: 4000 });\n if (payload.actions_url) window.open(payload.actions_url, \"_blank\", \"noopener,noreferrer\");\n await refreshConfig();\n } catch (error) {\n showToast(`${t(\"dispatchFailed\")}: ${error.message}`, { duration: 12000 });\n }\n }\n\n async function handleLogout() {\n await fetch(\"/api/logout\", { method: \"POST\" });\n window.location.reload();\n }\n\n function summaryText() {\n const inputs = buildInputs();\n return summaryRows(inputs).map(([label, value]) => `${label}: ${value}`).join(\"\\\n\");\n }\n\n document.querySelectorAll(\"[data-view]\").forEach((button) => button.addEventListener(\"click\", () => {\n state.view = [\"control\", \"health\", \"switch\"].includes(button.dataset.view) ? button.dataset.view : \"control\";\n renderConsoleView();\n if (state.view === \"control\") {\n refreshControlPlane();\n refreshOwnerDecisions();\n refreshResearchTasks();\n }\n if (state.view === \"health\") refreshHealth();\n }));\n\n document.querySelectorAll(\"[data-health-filter]\").forEach((button) => button.addEventListener(\"click\", () => {\n document.querySelectorAll(\"[data-health-filter]\").forEach((node) => node.classList.remove(\"active\"));\n button.classList.add(\"active\");\n state.health.filter = button.dataset.healthFilter;\n renderHealth();\n }));\n\n el(\"control-plane-list\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-owner-decision]\");\n if (!button || button.disabled) return;\n recordOwnerDecision(button);\n });\n\n el(\"platform-strip\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-platform]\");\n if (!button) return;\n state.selected = button.dataset.platform;\n state.forms[state.selected].strategyTouched = false;\n render();\n });\n\n el(\"account-select\").addEventListener(\"change\", () => {\n state.forms[state.selected].accountKey = el(\"account-select\").value;\n state.forms[state.selected].runtimeTargetTouched = false;\n state.forms[state.selected].reservedCashTouched = false;\n state.forms[state.selected].incomeLayerTouched = false;\n state.forms[state.selected].optionOverlayTouched = false;\n state.forms[state.selected].cashOnlyExecutionTouched = false;\n state.forms[state.selected].dcaTouched = false;\n state.forms[state.selected].strategyTouched = false;\n syncStrategyForAccount(state.selected);\n render();\n });\n\n el(\"strategy-select\").addEventListener(\"change\", () => {\n state.forms[state.selected].strategy = el(\"strategy-select\").value;\n state.forms[state.selected].strategyTouched = true;\n state.forms[state.selected].incomeLayerTouched = false;\n state.forms[state.selected].optionOverlayTouched = false;\n state.forms[state.selected].dcaTouched = false;\n syncIncomeLayerForAccount(state.selected);\n syncOptionOverlayForAccount(state.selected);\n syncDcaForAccount(state.selected);\n render();\n });\n\n el(\"mode-control\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-mode]\");\n if (!button || button.disabled) return;\n if (!supportedExecutionModesForPlatform(state.selected).includes(button.dataset.mode)) return;\n state.forms[state.selected].executionMode = button.dataset.mode;\n render();\n });\n\n el(\"plugin-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.pluginMode = normalizePluginMode(el(\"plugin-mode-select\").value);\n render();\n });\n\n el(\"runtime-target-enabled-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.runtimeTargetMode = normalizeRuntimeTargetMode(el(\"runtime-target-enabled-select\").value);\n form.runtimeTargetTouched = form.runtimeTargetMode !== \"current\";\n render();\n });\n\n el(\"income-layer-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerMode = normalizeIncomeLayerMode(el(\"income-layer-mode-select\").value);\n form.incomeLayerTouched = form.incomeLayerMode !== \"current\";\n if (form.incomeLayerMode === \"current\") {\n form.incomeLayerTouched = false;\n syncIncomeLayerForAccount(state.selected);\n }\n render();\n });\n\n el(\"income-layer-start-usd-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerTouched = true;\n form.incomeLayerMode = \"enabled\";\n form.incomeLayerStartUsd = el(\"income-layer-start-usd-input\").value.trim();\n render();\n });\n\n el(\"income-layer-max-ratio-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerTouched = true;\n form.incomeLayerMode = \"enabled\";\n form.incomeLayerMaxRatio = el(\"income-layer-max-ratio-input\").value.trim();\n render();\n });\n\n el(\"option-overlay-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.optionOverlayMode = normalizeOptionOverlayMode(el(\"option-overlay-mode-select\").value);\n form.optionOverlayTouched = form.optionOverlayMode !== \"current\";\n render();\n });\n\n el(\"cash-only-execution-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.cashOnlyExecutionMode = normalizeCashOnlyExecutionMode(el(\"cash-only-execution-mode-select\").value);\n form.cashOnlyExecutionTouched = form.cashOnlyExecutionMode !== \"current\";\n if (allowMarginExplicitlySelected(form)) reconcileExecutionCashPolicy(form, \"margin\");\n else restoreReserveAfterMarginDisabled(form);\n render();\n });\n\n el(\"dca-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.dcaTouched = true;\n form.dcaMode = normalizeDcaMode(el(\"dca-mode-select\").value);\n render();\n });\n\n el(\"dca-base-investment-usd-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.dcaTouched = true;\n form.dcaBaseInvestmentUsd = el(\"dca-base-investment-usd-input\").value.trim();\n render();\n });\n\n el(\"reserve-policy-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.reservePolicyMode = normalizeReservePolicyMode(el(\"reserve-policy-mode-select\").value);\n form.reservedCashTouched = form.reservePolicyMode !== \"current\";\n if (form.reservePolicyMode === \"current\") syncReservePolicyForAccount(state.selected);\n reconcileExecutionCashPolicy(form, \"reserve\");\n render();\n });\n\n el(\"min-reserved-cash-input\").addEventListener(\"input\", () => {\n state.forms[state.selected].reservedCashTouched = true;\n state.forms[state.selected].minReservedCashUsd = el(\"min-reserved-cash-input\").value.trim();\n render();\n });\n\n el(\"reserved-cash-ratio-input\").addEventListener(\"input\", () => {\n state.forms[state.selected].reservedCashTouched = true;\n state.forms[state.selected].reservedCashRatio = el(\"reserved-cash-ratio-input\").value.trim();\n render();\n });\n\n el(\"copy-button\").addEventListener(\"click\", async () => {\n try {\n await navigator.clipboard.writeText(summaryText());\n showToast(t(\"copied\"), { duration: 3000 });\n } catch {\n showToast(summaryText(), { duration: 0 });\n }\n });\n\n el(\"dispatch-button\").addEventListener(\"click\", dispatchSwitch);\n el(\"logout-button\").addEventListener(\"click\", handleLogout);\n el(\"lang-button\").addEventListener(\"click\", () => {\n state.lang = state.lang === \"zh\" ? \"en\" : \"zh\";\n localStorage.setItem(\"qsl-switch-lang\", state.lang);\n render();\n });\n\n applyStrategyProfiles(defaultStrategyProfiles);\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n render();\n boot();\n\n async function boot() {\n try {\n await refreshStrategyProfiles();\n await refreshSession();\n } catch {\n state.auth = { available: false, allowed: false, admin: false, login: null };\n state.configSource = \"default\";\n state.currentStrategies = {};\n state.bootMessageKey = \"bootTimeout\";\n state.appReady = true;\n render();\n }\n }\n"; +export const APP_JS = "\n\n let platformMeta = {\n binance: { label: \"Binance\", code: \"BN\", accent: \"var(--bn)\" },\n firstrade: { label: \"Firstrade\", code: \"FT\", accent: \"var(--ft)\" },\n ibkr: { label: \"IBKR\", code: \"IB\", accent: \"var(--ib)\" },\n longbridge: { label: \"LongBridge\", code: \"LB\", accent: \"var(--lb)\" },\n qmt: { label: \"QMT\", code: \"QM\", accent: \"var(--qmt)\" },\n schwab: { label: \"Schwab\", code: \"SW\", accent: \"var(--sw)\" },\n };\n\n const platformRepositories = {\n binance: \"QuantStrategyLab/BinancePlatform\",\n firstrade: \"QuantStrategyLab/FirstradePlatform\",\n ibkr: \"QuantStrategyLab/InteractiveBrokersPlatform\",\n longbridge: \"QuantStrategyLab/LongBridgePlatform\",\n qmt: \"QuantStrategyLab/QmtPlatform\",\n schwab: \"QuantStrategyLab/CharlesSchwabPlatform\",\n };\n // Alias for backward compatibility\n const defaultRepositories = platformRepositories;\n\n const defaultAccountOptions = window.__DEFAULT_ACCOUNT_OPTIONS__ || {\n binance: [{\"key\": \"default\", \"label\": \"Binance\", \"target_name\": \"default\", \"cash_currency\": \"USD\", \"supported_domains\": [\"crypto\"]}],\n firstrade: [{\"key\": \"preview\", \"label\": \"Firstrade\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\", \"service_name\": \"firstrade-quant-service\"}],\n ibkr: [{\"key\": \"preview\", \"label\": \"IBKR\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\", \"hk_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\"}],\n longbridge: [{\"key\": \"preview\", \"label\": \"LongBridge\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\", \"hk_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\"}],\n qmt: [{\"key\": \"default\", \"label\": \"QMT\", \"target_name\": \"default\", \"cash_currency\": \"CNY\", \"supported_domains\": [\"cn_equity\"], \"service_name\": \"qmt-quant-service\"}],\n schwab: [{\"key\": \"preview\", \"label\": \"Schwab\", \"target_name\": \"preview\", \"supported_domains\": [\"us_equity\"], \"cash_currency\": \"USD\", \"default_execution_mode\": \"live\", \"service_name\": \"charles-schwab-quant-service\"}],\n };\n\n const domainLabels = window.__DOMAIN_LABELS__ || {\n cn_equity: { zh: \"A股\", en: \"CN A-share\" },\n crypto: { zh: \"加密\", en: \"Crypto\" },\n hk_equity: { zh: \"港股\", en: \"HK Equity\" },\n us_equity: { zh: \"美股\", en: \"US Equity\" },\n };\n\n const platformConfig = window.__PLATFORM_CONFIG__ || {\n binance: {\n dry_run_only: false,\n margin_policy: false,\n reserved_cash: false,\n income_layer: false,\n option_overlay: false,\n dca: false,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n firstrade: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"firstrade-quant-service\",\n default_execution_mode: \"live\"\n },\n ibkr: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n longbridge: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"\",\n default_execution_mode: \"live\"\n },\n qmt: {\n dry_run_only: true,\n margin_policy: false,\n reserved_cash: false,\n income_layer: false,\n option_overlay: false,\n dca: false,\n execution_mode: \"paper\",\n service_name: \"qmt-quant-service\",\n default_execution_mode: \"paper\"\n },\n schwab: {\n dry_run_only: false,\n margin_policy: true,\n reserved_cash: true,\n income_layer: true,\n option_overlay: true,\n dca: true,\n execution_mode: \"live\",\n service_name: \"charles-schwab-quant-service\",\n default_execution_mode: \"live\"\n },\n };\n const runtimeAuthorityStatus = window.__QSL_RUNTIME_AUTHORITY_STATUS__ || {};\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n const reservePolicyModes = [\"none\", \"ratio\", \"floor\", \"max\"];\n const incomeLayerModes = [\"enabled\", \"disabled\"];\n const optionOverlayModes = [\"enabled\", \"disabled\"];\n const cashOnlyExecutionModes = [\"enabled\", \"disabled\"];\n const runtimeTargetModes = [\"enabled\", \"disabled\"];\n const pluginModes = [\"none\"];\n const dcaModes = [\"fixed\", \"smart\"];\n const runtimeTargetEnabledVariable = \"RUNTIME_TARGET_ENABLED\";\n const incomeLayerEnabledVariable = \"INCOME_LAYER_ENABLED\";\n const incomeLayerStartUsdVariable = \"INCOME_LAYER_START_USD\";\n const incomeLayerMaxRatioVariable = \"INCOME_LAYER_MAX_RATIO\";\n const dcaProfileDefaults = window.__DCA_PROFILE_DEFAULTS__ || {\n nasdaq_sp500_smart_dca: { defaultMode: \"fixed\", defaultBaseInvestmentUsd: \"1000\" },\n ibit_smart_dca: { defaultMode: \"fixed\", defaultBaseInvestmentUsd: \"1000\" },\n };\n const APP_BOOT_TIMEOUT_MS = 15000;\n const platformMinReservedCashVariables = {\n longbridge: \"LONGBRIDGE_MIN_RESERVED_CASH_USD\",\n ibkr: \"IBKR_MIN_RESERVED_CASH_USD\",\n schwab: \"SCHWAB_MIN_RESERVED_CASH_USD\",\n firstrade: \"FIRSTRADE_MIN_RESERVED_CASH_USD\",\n };\n const platformReservedCashRatioVariables = {\n longbridge: \"LONGBRIDGE_RESERVED_CASH_RATIO\",\n ibkr: \"IBKR_RESERVED_CASH_RATIO\",\n schwab: \"SCHWAB_RESERVED_CASH_RATIO\",\n firstrade: \"FIRSTRADE_RESERVED_CASH_RATIO\",\n };\n\n const defaultStrategyProfiles = window.__DEFAULT_STRATEGY_PROFILES__ || [\n {\n \"profile\": \"tqqq_growth_income\",\n \"label\": \"纳斯达克增长收益\",\n \"label_en\": \"NASDAQ Growth Income\",\n \"label_zh\": \"纳斯达克增长收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"250000\",\n \"income_layer_max_ratio\": \"0.55\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.3,\n \"DGRO\": 0.2,\n \"SGOV\": 0.4,\n \"SPYI\": 0.08,\n \"QQQI\": 0.02\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"tqqq_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"250000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.03\"\n },\n {\n \"profile\": \"soxl_soxx_trend_income\",\n \"label\": \"半导体趋势收益\",\n \"label_en\": \"Semiconductor Trend Income\",\n \"label_zh\": \"半导体趋势收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"150000\",\n \"income_layer_max_ratio\": \"0.95\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.15,\n \"DGRO\": 0.1,\n \"SGOV\": 0.7,\n \"SPYI\": 0.04,\n \"QQQI\": 0.01\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_income_overlay_enabled\": true,\n \"option_income_overlay_recipe\": \"soxx_put_credit_spread_income_v1\",\n \"option_income_overlay_start_usd\": \"150000\",\n \"option_income_overlay_nav_risk_ratio\": \"0.01\"\n },\n {\n \"profile\": \"nasdaq_sp500_smart_dca\",\n \"label\": \"纳指标普定投\",\n \"label_en\": \"NASDAQ/S&P 500 DCA\",\n \"label_zh\": \"纳指标普定投\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"dca_enabled\": true,\n \"dca_default_mode\": \"fixed\",\n \"dca_default_base_investment_usd\": \"1000\"\n },\n {\n \"profile\": \"ibit_smart_dca\",\n \"label\": \"IBIT比特币定投\",\n \"label_en\": \"IBIT Bitcoin DCA\",\n \"label_zh\": \"IBIT比特币定投\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"dca_enabled\": true,\n \"dca_default_mode\": \"fixed\",\n \"dca_default_base_investment_usd\": \"1000\"\n },\n {\n \"profile\": \"global_etf_rotation\",\n \"label\": \"全球ETF轮动\",\n \"label_en\": \"Global ETF Rotation\",\n \"label_zh\": \"全球ETF轮动\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"income_layer_start_usd\": \"500000\",\n \"income_layer_max_ratio\": \"0.15\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.4,\n \"DGRO\": 0.25,\n \"SGOV\": 0.3,\n \"SPYI\": 0.05\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"500000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"russell_top50_leader_rotation\",\n \"label\": \"罗素Top50领涨\",\n \"label_en\": \"Russell Top50 Leaders\",\n \"label_zh\": \"罗素Top50领涨\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\",\n \"income_layer_start_usd\": \"300000\",\n \"income_layer_max_ratio\": \"0.25\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.45,\n \"DGRO\": 0.3,\n \"SGOV\": 0.25\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"300000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"tecl_xlk_trend_income\",\n \"label\": \"TECL/XLK趋势收益\",\n \"label_en\": \"TECL/XLK Trend Income\",\n \"label_zh\": \"TECL/XLK趋势收益\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"failed_promotion_vs_live_profiles\"\n },\n {\n \"profile\": \"us_equity_combo\",\n \"label\": \"美股核心组合\",\n \"label_en\": \"US Core Combo\",\n \"label_zh\": \"美股核心组合\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": true,\n \"option_overlay_enabled\": true,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\",\n \"income_layer_start_usd\": \"300000\",\n \"income_layer_max_ratio\": \"0.25\",\n \"income_layer_allocations\": {\n \"SCHD\": 0.25,\n \"DGRO\": 0.25,\n \"SGOV\": 0.2,\n \"SPYI\": 0.15,\n \"QQQI\": 0.15\n },\n \"option_overlay_live_gate\": \"promotion_required\",\n \"option_overlay_live_status\": \"research_only\",\n \"option_growth_overlay_enabled\": true,\n \"option_growth_overlay_recipe\": \"spy_leaps_growth_v1\",\n \"option_growth_overlay_start_usd\": \"300000\",\n \"option_growth_overlay_nav_budget_ratio\": \"0.015\"\n },\n {\n \"profile\": \"us_equity_combo_core\",\n \"label\": \"美股核心组合影子\",\n \"label_en\": \"US Core Combo Shadow\",\n \"label_zh\": \"美股核心组合影子\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"us_equity_combo_leveraged\",\n \"label\": \"美股加速组合\",\n \"label_en\": \"US Alpha Combo\",\n \"label_zh\": \"美股加速组合\",\n \"domain\": \"us_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"hk_global_etf_tactical_rotation\",\n \"label\": \"港股ETF战术轮动\",\n \"label_en\": \"HK ETF Tactical Rotation\",\n \"label_zh\": \"港股ETF战术轮动\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"hk_low_vol_dividend_quality_snapshot\",\n \"label\": \"港股红利质量\",\n \"label_en\": \"HK Dividend Quality\",\n \"label_zh\": \"港股红利质量\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"hk_equity_combo\",\n \"label\": \"港股恒生组合\",\n \"label_en\": \"HK Core Combo\",\n \"label_zh\": \"港股恒生组合\",\n \"domain\": \"hk_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"cn_industry_etf_rotation\",\n \"label\": \"A股行业ETF轮动\",\n \"label_en\": \"CN Industry ETF Rotation\",\n \"label_zh\": \"A股行业ETF轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"cn_industry_etf_rotation_aggressive\",\n \"label\": \"A股ETF轮动\",\n \"label_en\": \"CN ETF Rotation\",\n \"label_zh\": \"A股ETF轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"live_candidate\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"live_candidate_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_index_etf_tactical_rotation\",\n \"label\": \"A股宽基ETF战术轮动\",\n \"label_en\": \"CN Index ETF Tactical Rotation\",\n \"label_zh\": \"A股宽基ETF战术轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_tactical_rotation\",\n \"label\": \"创业板战术轮动\",\n \"label_en\": \"CN ChiNext Tactical Rotation\",\n \"label_zh\": \"创业板战术轮动\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_growth_momentum_quality\",\n \"label\": \"创业板成长动量质量\",\n \"label_en\": \"CN ChiNext Growth Momentum Quality\",\n \"label_zh\": \"创业板成长动量质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_dividend_quality_snapshot\",\n \"label\": \"A股红利质量\",\n \"label_en\": \"CN Dividend Quality\",\n \"label_zh\": \"A股红利质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_chinext_growth_momentum_quality_snapshot\",\n \"label\": \"创业板成长质量快照\",\n \"label_en\": \"CN ChiNext Growth Quality Snapshot\",\n \"label_zh\": \"创业板成长质量快照\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_star_growth_momentum_quality\",\n \"label\": \"科创板成长动量质量\",\n \"label_en\": \"CN STAR Growth Momentum Quality\",\n \"label_zh\": \"科创板成长动量质量\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"cn_equity_combo\",\n \"label\": \"A股进取组合\",\n \"label_en\": \"CN Alpha Combo\",\n \"label_zh\": \"A股进取组合\",\n \"domain\": \"cn_equity\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n },\n {\n \"profile\": \"crypto_live_pool_rotation\",\n \"label\": \"加密实时池轮动\",\n \"label_en\": \"Crypto Live Pool Rotation\",\n \"label_zh\": \"加密实时池轮动\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"missing_current_promotion_evidence_and_preauthorized_autonomy_policy\"\n },\n {\n \"profile\": \"crypto_btc_dca\",\n \"label\": \"BTC定投\",\n \"label_en\": \"BTC DCA\",\n \"label_zh\": \"BTC定投\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"shadow_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"paper\",\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"shadow_candidate_requires_evidence_package\"\n },\n {\n \"profile\": \"crypto_trend_rotation\",\n \"label\": \"山寨趋势轮动\",\n \"label_en\": \"Altcoin Trend\",\n \"label_zh\": \"山寨趋势轮动\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": false,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\"\n },\n {\n \"profile\": \"crypto_equity_combo\",\n \"label\": \"加密动量组合\",\n \"label_en\": \"Crypto Core Combo\",\n \"label_zh\": \"加密动量组合\",\n \"domain\": \"crypto\",\n \"runtime_enabled\": false,\n \"income_layer_enabled\": false,\n \"option_overlay_enabled\": false,\n \"combo_enabled\": true,\n \"lifecycle_stage\": \"research_active\",\n \"can_switch_live\": false,\n \"allowed_execution_modes\": [\n \"dry_run\"\n ],\n \"blocked_live_reason\": \"research_backtest_only_requires_evidence_package\",\n \"combo_mode\": \"dynamic\"\n }\n ];\n\n const localStrategyLabels = {\n tqqq_growth_income: { zh: \"纳斯达克增长收益\", en: \"NASDAQ Growth Income\" },\n soxl_soxx_trend_income: { zh: \"半导体趋势收益\", en: \"Semiconductor Trend Income\" },\n nasdaq_sp500_smart_dca: { zh: \"纳指标普定投\", en: \"NASDAQ/S&P 500 DCA\" },\n ibit_smart_dca: { zh: \"IBIT比特币定投\", en: \"IBIT Bitcoin DCA\" },\n global_etf_rotation: { zh: \"全球ETF轮动\", en: \"Global ETF Rotation\" },\n russell_top50_leader_rotation: { zh: \"罗素Top50领涨\", en: \"Russell Top50 Leaders\" },\n hk_global_etf_tactical_rotation: { zh: \"港股ETF战术轮动\", en: \"HK ETF Tactical Rotation\" },\n hk_low_vol_dividend_quality_snapshot: { zh: \"港股红利质量\", en: \"HK Dividend Quality\" },\n cn_industry_etf_rotation: { zh: \"A股行业ETF轮动\", en: \"CN Industry ETF Rotation\" },\n cn_dividend_quality_snapshot: { zh: \"A股红利质量\", en: \"CN Dividend Quality\" },\n us_equity_combo: { zh: \"美股核心组合\", en: \"US Core Combo\" },\n us_equity_combo_leveraged: { zh: \"美股加速组合\", en: \"US Alpha Combo\" },\n hk_equity_combo: { zh: \"港股恒生组合\", en: \"HK Core Combo\" },\n cn_industry_etf_rotation_aggressive: { zh: \"A股ETF轮动\", en: \"CN ETF Rotation\" },\n cn_stock_momentum_rotation: { zh: \"A股个股动量\", en: \"CN Stock Momentum\" },\n cn_equity_combo: { zh: \"A股进取组合\", en: \"CN Alpha Combo\" },\n crypto_btc_dca: { zh: \"BTC定投\", en: \"BTC DCA\" },\n crypto_trend_rotation: { zh: \"山寨趋势轮动\", en: \"Altcoin Trend\" },\n crypto_equity_combo: { zh: \"加密动量组合\", en: \"Crypto Core Combo\" },\n };\n\n const fallbackIncomeLayerDefaults = window.__INCOME_LAYER_DEFAULTS__ || {\n tqqq_growth_income: {\n startUsd: 250000,\n maxRatio: \"0.55\",\n allocations: { SCHD: 0.30, DGRO: 0.20, SGOV: 0.40, SPYI: 0.08, QQQI: 0.02 },\n },\n soxl_soxx_trend_income: {\n startUsd: 150000,\n maxRatio: \"0.95\",\n allocations: { SCHD: 0.15, DGRO: 0.10, SGOV: 0.70, SPYI: 0.04, QQQI: 0.01 },\n },\n global_etf_rotation: {\n startUsd: 500000,\n maxRatio: \"0.15\",\n allocations: { SCHD: 0.40, DGRO: 0.25, SGOV: 0.30, SPYI: 0.05 },\n },\n russell_top50_leader_rotation: {\n startUsd: 300000,\n maxRatio: \"0.25\",\n allocations: { SCHD: 0.45, DGRO: 0.30, SGOV: 0.25 },\n },\n us_equity_combo: {\n startUsd: 300000,\n maxRatio: \"0.25\",\n allocations: { SCHD: 0.25, DGRO: 0.25, SGOV: 0.20, SPYI: 0.15, QQQI: 0.15 },\n }};\n let incomeLayerDefaults = {};\n const fallbackOptionOverlayDefaults = window.__OPTION_OVERLAY_DEFAULTS__ || {\n tqqq_growth_income: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"tqqq_leaps_growth_v1\", startUsd: \"250000\", ratio: \"0.03\", ratioKind: \"budget\" },\n ],\n },\n soxl_soxx_trend_income: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"income\", recipe: \"soxx_put_credit_spread_income_v1\", startUsd: \"150000\", ratio: \"0.01\", ratioKind: \"risk\" },\n ],\n },\n global_etf_rotation: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"500000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n russell_top50_leader_rotation: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"300000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n us_equity_combo: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [\n { family: \"growth\", recipe: \"spy_leaps_growth_v1\", startUsd: \"300000\", ratio: \"0.015\", ratioKind: \"budget\" },\n ],\n },\n us_equity_combo_leveraged: {\n liveGate: \"promotion_required\",\n liveStatus: \"research_only\",\n families: [],\n }};\n let optionOverlayDefaults = {};\n\n const strategyDomains = [\"us_equity\", \"hk_equity\", \"cn_equity\", \"crypto\"];\n let strategyOptions = [];\n let strategyLabels = {};\n let strategyCatalog = {};\n\n\n const copy = {\n zh: {\n appTitle: \"QuantStrategyLab\",\n appSubtitle: \"已验证证据、运行许可和所有者决策。\",\n controlPlaneView: \"待处理\",\n healthView: \"运行保障\",\n switchView: \"运行计划\",\n controlPlaneEyebrow: \"今日待处理 / 只读\",\n controlPlaneTitle: \"只处理需要你决定的事。\",\n controlPlaneSubtitle: \"这里不是交易大屏:只显示经验证的待办、证据是否足够,以及需要人工做出的边界决定。\",\n controlCandidateTotal: \"候选总数\",\n controlDeferred: \"本次延期\",\n controlParked: \"已停车\",\n controlOwnerDecision: \"等待我决定\",\n controlCandidateBoard: \"需要跟进\",\n controlDataReady: \"快照已同步(不代表策略可运行)\",\n controlDataStale: \"快照已过期\",\n controlDataUnavailable: \"等待可用快照\",\n controlAttentionResearchOnly: \"仅研究,未授予执行权限\",\n controlAttentionRequired: \"研究状态需要关注\",\n controlAttentionUnavailable: \"尚无已验证研究状态\",\n controlComputedAt: \"最近计算:{time}\",\n controlLoginNotice: \"登录后读取私有全局快照;没有快照时不会展示虚构信息。\",\n controlStaleNotice: \"全局快照已超过允许的新鲜度窗口;页面保留原始记录,但不把它作为当前结论。\",\n controlUnavailableNotice: \"还没有可用的全局快照;当前页面保持 fail-closed 空状态。\",\n controlUpstreamNotice: \"快照已加载,但有 {count} 个上游提示;缺失信息不会被补成推断结论。\",\n controlAttentionNotice: \"研究状态需要关注(延期 {deferred}、停车 {parked}、信号 {signals})。快照送达不等于策略验证或执行授权;系统保持不交易。\",\n controlEmptyCandidates: \"暂无可展示的候选快照。\",\n controlNoRecommendation: \"没有可用的机器建议。\",\n controlStageMeta: \"阶段:{stage} · 状态:{status} · 证据:{freshness}\",\n controlNext: \"下一步\",\n ownerDecisionTitle: \"所有者决定\",\n ownerDecisionAdminOnly: \"只有控制台管理员可以记录决定。\",\n ownerDecisionReady: \"当前证据已绑定;请人工选择后记录不可执行意图。\",\n ownerDecisionRecorded: \"已记录:{decision}(仅意图,不会启用实盘)\",\n ownerDecisionApprove: \"批准受限试运行意图\",\n ownerDecisionPark: \"保持暂停\",\n ownerDecisionRetire: \"退役候选\",\n ownerDecisionConfirm: \"这只会记录不可执行的所有者决定意图,不会下单、改资金或启用实盘。继续吗?\",\n ownerDecisionSubmitting: \"正在记录决定…\",\n ownerDecisionSuccess: \"所有者决定已记录;它不会自动启用实盘。\",\n ownerDecisionFailed: \"无法记录所有者决定\",\n executionEvidenceBoard: \"按需查看策略 × 平台证据覆盖\",\n executionEvidenceLoginNotice: \"登录后读取私有执行证据快照;缺失来源不会被推断成 paper 或 live 状态。\",\n executionEvidenceStaleNotice: \"执行证据已超过允许的新鲜度窗口;它保留为历史记录,不能作为当前运行或 P6 结论。\",\n executionEvidenceUnavailableNotice: \"还没有可用的执行证据来源;当前列表保持 fail-closed 空状态。\",\n executionEvidenceUpstreamNotice: \"执行证据已加载,但有 {count} 个上游提示;页面不会补造平台能力或运行资格。\",\n executionEvidenceEmpty: \"暂无可展示的策略 × 平台执行证据。\",\n executionEvidenceMeta: \"{platform} · 当前通道:{environment} · 来源:{source}\",\n executionEvidenceDetail: \"策略:{strategy} · 数据:{data} · 执行:{execution} · Shadow:{shadow} · Paper:{paper}\",\n executionEvidenceNoOrder: \"固定边界:只读证据;不包含账户、订单、资金或 P6 实盘授权。\",\n executionEvidenceNext: \"下一步\",\n m0ResearchBoard: \"按需查看 M0 研究观察\",\n m0ResearchLoginNotice: \"登录后读取私有 M0 研究台账;缺失台账不会被补成研究结论。\",\n m0ResearchStaleNotice: \"M0 研究观察已超过有效期,仅保留为历史上下文。\",\n m0ResearchUnavailableNotice: \"还没有可用的 M0 研究台账;页面保持 fail-closed 空状态。\",\n m0ResearchUpstreamNotice: \"M0 研究台账已加载,但有 {count} 个上游提示;页面不会补造研究结论。\",\n m0ResearchEmpty: \"暂无可展示的已验证 M0 研究观察。\",\n m0ResearchMore: \"仅显示前 {count} 条研究观察;其余仍保留在只读台账中。\",\n m0ResearchMeta: \"{kind} · 查看于:{viewed}\",\n m0ResearchStateFreshness: \"状态:{state} · 新鲜度:{freshness}\",\n m0ResearchHorizons: \"主要观察期:{primary} · 适用观察期:{suitable}\",\n m0ResearchEvidence: \"置信:{confidence} · 风格:{style} · 资料摘要:{digest}\",\n m0ResearchConsistency: \"当前周期分歧:{conflict} · 历史失效漂移:{drift}\",\n m0ResearchNoOrder: \"固定边界:仅研究、无订单;不产生任何运行动作。\",\n adaptiveSelectionBoard: \"按需查看 M1 Shadow 建议\",\n adaptiveSelectionLoginNotice: \"登录后读取私有 Shadow 建议;缺失来源不会被补成策略或订单。\",\n adaptiveSelectionStaleNotice: \"此 Shadow 建议已超出新鲜度窗口,仅保留为历史上下文。\",\n adaptiveSelectionUnavailableNotice: \"还没有可用的 M1 Shadow 建议;页面保持 fail-closed 空状态。\",\n adaptiveSelectionUpstreamNotice: \"Shadow 建议带有 {count} 个上游提示;页面不会推断策略、平台或订单。\",\n adaptiveSelectionEmpty: \"暂无可展示的 Shadow 建议。\",\n adaptiveSelectionMeta: \"{source} · {domain} · 市场截至 {asOf}\",\n adaptiveSelectionRecommended: \"建议观察\",\n adaptiveSelectionNoCandidate: \"无合格 Shadow 候选\",\n adaptiveSelectionReason: \"原因:{reasons}\",\n adaptiveSelectionNoOrder: \"固定边界:仅供人工查看,零建议权重,不改策略、平台、资金、运行状态或订单。\",\n researchTaskBoard: \"按需查看研究任务队列\",\n researchTaskLoginNotice: \"登录后读取私有研究任务索引;没有来源快照时不会展示虚构任务。\",\n researchTaskStaleNotice: \"研究任务来源已超过允许的新鲜度窗口;任务保留为历史记录,但不作为当前工作指令。\",\n researchTaskUnavailableNotice: \"还没有可用的研究任务来源;当前队列保持 fail-closed 空状态。\",\n researchTaskUpstreamNotice: \"研究任务索引已加载,但有 {count} 个上游提示;页面不会补造任务。\",\n researchTaskEmpty: \"暂无可展示的已验证研究任务。\",\n researchTaskMeta: \"{type} · {domain} · 创建于:{created}\",\n researchTaskLimits: \"离线研究:最多 {runs} 次 / {seconds} 秒 · P1 {p1} · P2 {p2} · P3 {p3}\",\n researchTaskNoOrder: \"固定边界:仅研究、零仓位、无订单;不进入 P4/P5/P6。\",\n healthEyebrow: \"运行保障 / 只读\",\n healthTitle: \"先看机器结论,再决定是否介入。\",\n healthSubtitle: \"健康不是实盘许可;它只告诉你是否要复核、降级或保持观察。\",\n runtimeAuthorityTitle: \"P0–P6 控制面授权状态\",\n runtimeAuthorityNotice: \"这里的 live/default_execution_mode/live_configured 是历史配置元数据,不是当前运行、订单、资金或阶段升级授权。\",\n runtimeAuthorityAsOf: \"截至:{asOf} · P1–P3 non-live 数据获取仍需独立、精确的契约;P4–P6 尚未定义。\",\n healthTotal: \"策略总数\",\n healthHealthy: \"健康\",\n healthWatch: \"观察\",\n healthReview: \"需要复核\",\n healthCritical: \"严重\",\n healthBoard: \"策略状态\",\n bootKicker: \"初始化控制台\",\n bootTitle: \"读取策略配置\",\n bootMessage: \"正在读取登录状态、账号配置和当前状态。\",\n bootStrategy: \"正在读取策略目录。\",\n bootSession: \"正在验证登录状态。\",\n bootConfig: \"正在读取账号配置和当前状态。\",\n bootTimeout: \"加载超时,已切换到公开预览(登录后可重试)。\",\n bootPublic: \"公开预览已就绪。\",\n login: \"登录\",\n logout: \"退出\",\n signedInAs: \"已登录 {login}\",\n planEyebrow: \"运行计划 / 受限变更\",\n planTitle: \"先定义边界,再提交配置。\",\n planSubtitle: \"一次只为一个平台和账号准备一个可审计的运行计划。页面只提交配置意图;运行、订单和实盘授权由独立的证据与阶段门槛决定。\",\n planScopeTitle: \"选择范围\",\n planScopeSubtitle: \"平台、账号、策略与目标环境\",\n planRuntimeTitle: \"运行保护\",\n planRuntimeSubtitle: \"停用、插件和附加层都受同一计划约束\",\n planOverlayTitle: \"策略附加层\",\n planOverlaySubtitle: \"仅使用策略已定义的默认边界\",\n planCashSubtitle: \"现金预留优先于融资;两者不能同时覆盖\",\n activePlatform: \"目标平台\",\n account: \"目标账号\",\n strategy: \"策略\",\n mode: \"运行环境\",\n live: \"实盘(需独立授权)\",\n paper: \"旧版非实盘\",\n dryRun: \"演练(不下单)\",\n liveModeUnavailable: \"当前没有同时满足策略发布、运行许可与证据门槛的实盘候选;请保持非实盘。\",\n runtimeTargetMode: \"账号运行状态\",\n runtimeSectionTitle: \"运行与插件\",\n runtimeTargetCurrent: \"沿用当前状态\",\n runtimeTargetEnabled: \"启用\",\n runtimeTargetDisabled: \"禁用\",\n runtimeTargetModeMeta: \"停用后正式运行会跳过,模拟运行和健康检查仍可用。\",\n pluginMode: \"插件状态\",\n pluginModeNone: \"不挂载旧插件\",\n pluginModeMeta: \"当前候选未绑定插件;旧插件不会自动挂载。\",\n incomeLayerMode: \"收入层状态\",\n incomeLayerSectionTitle: \"收入层\",\n incomeLayerCurrent: \"沿用当前配置\",\n incomeLayerEnabled: \"开启收入层\",\n incomeLayerDisabled: \"关闭收入层\",\n incomeLayerNotSupported: \"该策略未定义收入层\",\n incomeLayerStartUsd: \"收入层起始金额\",\n incomeLayerMaxRatio: \"收入层最高比例\",\n incomeLayerModeMeta: \"仅对已定义收入层的美股策略生效。\",\n incomeLayerDefaultMeta: \"策略默认:起始 {start},最高 {ratio}。\",\n incomeLayerAllocationMeta: \"默认分配:{allocations}。\",\n incomeLayerStartMeta: \"总资产达到该金额后启用收入层。\",\n incomeLayerRatioMeta: \"例如 0.55 表示最高 55%。\",\n optionOverlayMode: \"期权层状态\",\n optionOverlaySectionTitle: \"期权层\",\n optionOverlayCurrent: \"沿用当前配置\",\n optionOverlayEnabled: \"启用期权层\",\n optionOverlayDisabled: \"关闭期权层\",\n optionOverlayNotSupported: \"该策略未定义期权层\",\n optionOverlayModeMeta: \"启用时使用策略默认的最佳 recipe 和预算,不在这里手动调比例。\",\n optionOverlayDefaultMeta: \"{defaults}\",\n optionOverlayFamilyGrowth: \"增长\",\n optionOverlayFamilyIncome: \"收入\",\n optionOverlayBudgetRatio: \"预算 {ratio}\",\n optionOverlayRiskRatio: \"风险 {ratio}\",\n cashOnlyExecutionMode: \"允许融资\",\n cashOnlyExecutionCurrent: \"沿用当前配置\",\n cashOnlyExecutionYes: \"是\",\n cashOnlyExecutionNo: \"否\",\n cashOnlyExecutionModeMeta: \"选「否」时只按真实现金下单,不会动用 margin 购买力。\",\n cashOnlyExecutionValueYes: \"是\",\n cashOnlyExecutionValueNo: \"否\",\n currentCashOnlyExecution: \"当前允许融资\",\n pendingCashOnlyExecution: \"待提交允许融资\",\n executionCashPolicyTitle: \"资本边界\",\n executionCashPolicyNote: \"允许融资与预留现金覆盖不能同时生效;选「是」会清空预留覆盖,设预留覆盖会强制「否」。\",\n executionCashMarginBlocksReserve: \"已选允许融资;提交时会清空预留现金覆盖。\",\n executionCashReserveBlocksMargin: \"已设预留现金覆盖;提交时会强制不允许融资。\",\n qmtPlatformCashNote: \"A 股 QMT 不使用 margin / 平台预留现金;现金约束在策略参数 execution_cash_reserve_ratio 内配置。\",\n qmtDryRunOnlyNote: \"当前平台仅支持不下单演练,尚无可用的实盘执行通道。\",\n binancePlatformNote: \"Binance 平台不使用券商级收入层与期权层;相关功能由策略内部实现。\",\n invalidExecutionCashPolicyNote: \"允许融资与预留现金覆盖冲突,请只保留一种约束。\",\n dcaMode: \"定投模式\",\n dcaSectionTitle: \"定投\",\n dcaModeFixed: \"定额定投\",\n dcaModeSmart: \"智能定投\",\n dcaBaseInvestmentUsd: \"定投基准金额\",\n dcaModeMeta: \"仅定投策略可配置。\",\n dcaDefaultMeta: \"默认:{mode},基准金额 {amount}。\",\n dcaNotSupported: \"该策略不是定投策略\",\n dcaPlatformNotSupported: \"当前平台不支持定投策略\",\n currentDca: \"当前定投设置\",\n pendingDca: \"待提交定投设置\",\n dcaText: \"{mode},基准金额 {amount}\",\n minReservedCash: \"最小预留现金 ({currency})\",\n reservedCashRatio: \"预留现金比例\",\n reservedCashMode: \"预留现金策略\",\n reservePolicyCurrent: \"沿用当前配置\",\n reservePolicyNone: \"不设置平台预留现金\",\n reservePolicyRatio: \"仅按比例\",\n reservePolicyFloor: \"仅按固定金额\",\n reservePolicyMax: \"固定金额和比例取较大值\",\n reservedCashModeMeta: \"选择是否沿用、清空或覆盖平台预留现金。\",\n reservedCashNone: \"不设置\",\n reservedCashDefault: \"未配置(平台默认:0 {currency} / 0%)\",\n reservedCashMeta: \"固定金额下限,可单独设置或与比例取较大值。\",\n reservedCashRatioMeta: \"例如 0.03 表示 3%。\",\n summary: \"风险与变更摘要\",\n summaryCurrent: \"当前边界\",\n summaryPending: \"待提交变更\",\n planReadinessTitle: \"提交前核对\",\n planCheckAccount: \"账号与作用范围\",\n planCheckStrategy: \"策略与目标环境\",\n planCheckRisk: \"现金与风险边界\",\n planCheckAuthority: \"实盘执行授权\",\n planCheckWaiting: \"等待配置\",\n planCheckSelected: \"已读取\",\n planCheckValid: \"已校验\",\n planCheckFix: \"需修正\",\n planCheckNonLive: \"非实盘\",\n planCheckNoAuthority: \"未授予\",\n planAuditNote: \"提交记录会写入审计链;它不等同于订单、资金或实盘许可。\",\n copySummary: \"复制状态\",\n loginToRun: \"登录后提交计划\",\n loadingConfig: \"读取配置中\",\n configureAccounts: \"配置账号后切换\",\n runSwitch: \"提交受限变更\",\n noChanges: \"无变更\",\n readonlyNote: \"登录后才可提交受限配置。\",\n publicReadonly: \"登录后查看账号配置。\",\n loadingConfigNote: \"正在读取账号配置和当前状态。\",\n missingConfigNote: \"账号配置未加载,暂时不能执行。\",\n readyNote: \"提交的是受限配置变更;它不授予订单、资金或实盘权限。\",\n invalidStrategyNote: \"当前账号没有可执行策略,暂时不能切换。\",\n invalidReservePolicyNote: \"请为当前预留现金策略填写有效金额或比例。\",\n invalidIncomeLayerNote: \"请填写有效的收入层起始金额和最高比例。\",\n invalidOptionOverlayNote: \"当前策略未定义可启用的期权层。\",\n invalidDcaNote: \"请填写有效的定投模式和基准金额。\",\n publicOauthTitle: \"GitHub OAuth 保护\",\n publicOauthText: \"只允许白名单账号进入私有配置。\",\n publicWorkerTitle: \"Worker 端触发\",\n publicWorkerText: \"令牌保留在服务端,浏览器只提交切换意图。\",\n publicAuditTitle: \"变更可回溯\",\n publicAuditText: \"受限配置变更由 GitHub Actions 执行,便于审计和回滚。\",\n noAccount: \"没有账号选项\",\n noStrategy: \"没有支持的策略\",\n repository: \"平台仓库\",\n selectedAccount: \"账号\",\n selectedMarket: \"市场\",\n currentRuntimeTarget: \"当前账号状态\",\n pendingRuntimeTarget: \"待提交账号状态\",\n reservedCashPolicy: \"当前预留现金\",\n currentIncomeLayer: \"当前收入层\",\n pendingIncomeLayer: \"待提交收入层\",\n currentOptionOverlay: \"当前期权层\",\n pendingOptionOverlay: \"待提交期权层\",\n pendingReservedCashPolicy: \"待提交预留现金\",\n pendingMode: \"待提交模式\",\n currentPluginMode: \"当前插件范围\",\n pendingPluginMode: \"待提交插件范围\",\n unchanged: \"不变\",\n copied: \"已复制状态\",\n dispatching: \"正在触发工作流...\",\n dispatched: \"已触发工作流\",\n dispatchFailed: \"触发失败\",\n targetMeta: \"目标 {target} · 服务 {service} · 市场 {domains}\",\n strategyMeta: \"支持市场:{domains}\",\n strategyLifecycleMeta: \"当前门槛 {stage}\",\n strategyBlockedCountMeta: \"{count} 个策略未达 live 门槛\",\n strategyDefaultBlockedMeta: \"默认策略 {profile} 已阻断:{reason}\",\n usEquity: \"美股\",\n hkEquity: \"港股\",\n cnEquity: \"A股\",\n cryptoEquity: \"加密\",\n currentStrategy: \"当前策略\",\n nextStrategy: \"切换策略\",\n notRead: \"读取失败\",\n runtimeTargetOn: \"启用\",\n runtimeTargetOff: \"禁用\",\n incomeLayerDefault: \"开启,{start}起 {ratio}\",\n incomeLayerOff: \"关闭\",\n incomeLayerOn: \"开启,起始 {start},最高 {ratio}\",\n optionOverlayOff: \"关闭\",\n optionOverlayOn: \"开启\",\n optionOverlayDefaultSimple: \"开启\",\n optionOverlayDefault: \"开启,{detail}\",\n cashOnlyExecutionDefault: \"仅用现金\",\n },\n en: {\n appTitle: \"QuantStrategyLab\",\n appSubtitle: \"Verified evidence, runtime eligibility, and owner decisions.\",\n controlPlaneView: \"Attention\",\n healthView: \"Runtime guard\",\n switchView: \"Run plan\",\n controlPlaneEyebrow: \"Today / read only\",\n controlPlaneTitle: \"Only decide what needs you.\",\n controlPlaneSubtitle: \"This is not a trading screen: it shows verified follow-ups, evidence coverage, and the boundaries that need an owner decision.\",\n controlCandidateTotal: \"Candidates\",\n controlDeferred: \"Deferred now\",\n controlParked: \"Parked\",\n controlOwnerDecision: \"Needs my decision\",\n controlCandidateBoard: \"Follow-ups\",\n controlDataReady: \"Snapshot synced (not strategy readiness)\",\n controlDataStale: \"Snapshot is stale\",\n controlDataUnavailable: \"Waiting for a usable snapshot\",\n controlAttentionResearchOnly: \"Research only; no execution authority\",\n controlAttentionRequired: \"Research state needs attention\",\n controlAttentionUnavailable: \"No verified research state\",\n controlComputedAt: \"Last computed: {time}\",\n controlLoginNotice: \"Sign in to read the private global snapshot. Missing data is never invented.\",\n controlStaleNotice: \"This snapshot is outside its freshness window. The record remains visible, but is not a current conclusion.\",\n controlUnavailableNotice: \"No usable global snapshot is available yet. This page remains fail-closed and empty.\",\n controlUpstreamNotice: \"The snapshot loaded with {count} upstream notice(s); missing information is never inferred.\",\n controlAttentionNotice: \"Research needs attention ({deferred} deferred, {parked} parked, {signals} signal(s)). Snapshot delivery is not strategy validation or execution authority; the system remains no-order.\",\n controlEmptyCandidates: \"No candidate snapshot is available to display.\",\n controlNoRecommendation: \"No machine recommendation is available.\",\n controlStageMeta: \"Stage: {stage} · status: {status} · evidence: {freshness}\",\n controlNext: \"NEXT\",\n ownerDecisionTitle: \"Owner decision\",\n ownerDecisionAdminOnly: \"Only console administrators can record a decision.\",\n ownerDecisionReady: \"Current evidence is bound; choose an action to record a non-executable intent.\",\n ownerDecisionRecorded: \"Recorded: {decision} (intent only; Live remains disabled)\",\n ownerDecisionApprove: \"Approve limited-canary intent\",\n ownerDecisionPark: \"Keep parked\",\n ownerDecisionRetire: \"Retire candidate\",\n ownerDecisionConfirm: \"This records a non-executable owner intent only. It will not place orders, change funds, or enable Live. Continue?\",\n ownerDecisionSubmitting: \"Recording decision…\",\n ownerDecisionSuccess: \"Owner decision recorded; it does not enable Live automatically.\",\n ownerDecisionFailed: \"Could not record owner decision\",\n executionEvidenceBoard: \"View strategy × platform evidence on demand\",\n executionEvidenceLoginNotice: \"Sign in to read private execution-evidence snapshots. Missing sources never imply a paper or live state.\",\n executionEvidenceStaleNotice: \"This execution evidence is beyond its freshness window. It remains historical context, not a current runtime or P6 conclusion.\",\n executionEvidenceUnavailableNotice: \"No usable execution-evidence source is available yet. This list remains fail-closed and empty.\",\n executionEvidenceUpstreamNotice: \"Execution evidence loaded with {count} upstream notice(s); no platform capability or runtime authority is inferred.\",\n executionEvidenceEmpty: \"No strategy × platform execution evidence is available to display.\",\n executionEvidenceMeta: \"{platform} · current lane: {environment} · source: {source}\",\n executionEvidenceDetail: \"strategy: {strategy} · data: {data} · execution: {execution} · shadow: {shadow} · paper: {paper}\",\n executionEvidenceNoOrder: \"Fixed boundary: read-only evidence; no account, order, funds, or P6 live authority.\",\n executionEvidenceNext: \"NEXT\",\n m0ResearchBoard: \"View M0 research observations on demand\",\n m0ResearchLoginNotice: \"Sign in to read the private M0 research ledger. A missing ledger never implies a research conclusion.\",\n m0ResearchStaleNotice: \"These M0 research observations are beyond their validity window and remain historical context only.\",\n m0ResearchUnavailableNotice: \"No usable M0 research ledger is available yet. This view remains fail-closed and empty.\",\n m0ResearchUpstreamNotice: \"The M0 research ledger loaded with {count} upstream notice(s); no research conclusion is invented.\",\n m0ResearchEmpty: \"No verified M0 research observation is available to display.\",\n m0ResearchMore: \"Showing the first {count} research observations; the remainder stays in the read-only ledger.\",\n m0ResearchMeta: \"{kind} · viewed {viewed}\",\n m0ResearchStateFreshness: \"State: {state} · freshness: {freshness}\",\n m0ResearchHorizons: \"Primary horizon: {primary} · suitable horizons: {suitable}\",\n m0ResearchEvidence: \"Confidence: {confidence} · style: {style} · source digest: {digest}\",\n m0ResearchConsistency: \"Current horizon conflict: {conflict} · historical stale drift: {drift}\",\n m0ResearchNoOrder: \"Fixed boundary: research only, no order, and no runtime action.\",\n adaptiveSelectionBoard: \"View M1 Shadow suggestions on demand\",\n adaptiveSelectionLoginNotice: \"Sign in to read private Shadow suggestions. Missing sources never imply a strategy or order.\",\n adaptiveSelectionStaleNotice: \"This Shadow suggestion is beyond its freshness window and remains historical context only.\",\n adaptiveSelectionUnavailableNotice: \"No usable M1 Shadow suggestion is available yet. This view remains fail-closed and empty.\",\n adaptiveSelectionUpstreamNotice: \"Shadow suggestions include {count} upstream notice(s); no strategy, platform, or order is inferred.\",\n adaptiveSelectionEmpty: \"No Shadow suggestion is available to display.\",\n adaptiveSelectionMeta: \"{source} · {domain} · market as of {asOf}\",\n adaptiveSelectionRecommended: \"Observe suggestion\",\n adaptiveSelectionNoCandidate: \"No eligible Shadow candidate\",\n adaptiveSelectionReason: \"Reasons: {reasons}\",\n adaptiveSelectionNoOrder: \"Fixed boundary: human-readable only, zero proposed weight, no strategy, platform, funds, runtime, or order change.\",\n researchTaskBoard: \"View research queue on demand\",\n researchTaskLoginNotice: \"Sign in to read the private research task index. Missing source snapshots never imply a task.\",\n researchTaskStaleNotice: \"The research task source is beyond its freshness window. It remains historical context, not a current instruction.\",\n researchTaskUnavailableNotice: \"No research task source is available yet. The queue remains fail-closed and empty.\",\n researchTaskUpstreamNotice: \"The research task index loaded with {count} upstream notices; no task is inferred from missing data.\",\n researchTaskEmpty: \"No verified research task is available to display.\",\n researchTaskMeta: \"{type} · {domain} · created {created}\",\n researchTaskLimits: \"Offline research: up to {runs} run(s) / {seconds}s · P1 {p1} · P2 {p2} · P3 {p3}\",\n researchTaskNoOrder: \"Fixed boundary: research only, zero size, no order; never P4/P5/P6.\",\n healthEyebrow: \"Runtime guard / read only\",\n healthTitle: \"Read the machine conclusion before deciding to intervene.\",\n healthSubtitle: \"Health is not live approval. It only tells you whether to review, reduce, or keep watching.\",\n runtimeAuthorityTitle: \"P0–P6 control-plane authority\",\n runtimeAuthorityNotice: \"live/default_execution_mode/live_configured are legacy configuration metadata, not authorization for current runtime, orders, funds, or stage changes.\",\n runtimeAuthorityAsOf: \"As of {asOf} · P1–P3 non-live data acquisition still needs its own precise contract; P4–P6 are undefined.\",\n healthTotal: \"Strategies\",\n healthHealthy: \"Healthy\",\n healthWatch: \"Watch\",\n healthReview: \"Review\",\n healthCritical: \"Critical\",\n healthBoard: \"Strategy status\",\n bootKicker: \"Starting console\",\n bootTitle: \"Loading strategy config\",\n bootMessage: \"Reading session, account config, and current state.\",\n bootStrategy: \"Reading strategy catalog.\",\n bootSession: \"Checking sign-in status.\",\n bootConfig: \"Reading account config and current state.\",\n bootTimeout: \"Loading timed out; switched to public preview. Retry after signing in.\",\n bootPublic: \"Public preview is ready.\",\n login: \"Sign in\",\n logout: \"Sign out\",\n signedInAs: \"Signed in as {login}\",\n planEyebrow: \"Run plan / bounded change\",\n planTitle: \"Set the boundary before submitting configuration.\",\n planSubtitle: \"Prepare one auditable plan for one platform and account. This page submits configuration intent only; runtime, order, and live authority remain governed by independent evidence and lifecycle gates.\",\n planScopeTitle: \"Choose scope\",\n planScopeSubtitle: \"Platform, account, strategy, and target environment\",\n planRuntimeTitle: \"Runtime protection\",\n planRuntimeSubtitle: \"Disable state, plugins, and overlays share one plan boundary\",\n planOverlayTitle: \"Strategy overlays\",\n planOverlaySubtitle: \"Only strategy-defined default guardrails are available\",\n planCashSubtitle: \"Cash reserve takes precedence over margin; they cannot override together\",\n activePlatform: \"Target platform\",\n account: \"Target account\",\n strategy: \"Strategy\",\n mode: \"Target environment\",\n live: \"Live (independent approval required)\",\n paper: \"Legacy non-live\",\n dryRun: \"Dry run (no orders)\",\n liveModeUnavailable: \"No candidate currently meets the strategy-release, runtime-eligibility, and evidence gates for Live. Keep this plan non-live.\",\n runtimeTargetMode: \"Account status\",\n runtimeSectionTitle: \"Runtime and plugins\",\n runtimeTargetCurrent: \"Keep current status\",\n runtimeTargetEnabled: \"Enabled\",\n runtimeTargetDisabled: \"Disabled\",\n runtimeTargetModeMeta: \"Disabled accounts skip live runs; dry runs and health checks still work.\",\n pluginMode: \"Plugin status\",\n pluginModeNone: \"Do not mount legacy plugins\",\n pluginModeMeta: \"The current candidate has no bound plugin; legacy plugins are not auto-mounted.\",\n incomeLayerMode: \"Income layer\",\n incomeLayerSectionTitle: \"Income layer\",\n incomeLayerCurrent: \"Keep current config\",\n incomeLayerEnabled: \"Enable income layer\",\n incomeLayerDisabled: \"Disable income layer\",\n incomeLayerNotSupported: \"No income layer for this strategy\",\n incomeLayerStartUsd: \"Income layer start amount\",\n incomeLayerMaxRatio: \"Income layer max ratio\",\n incomeLayerModeMeta: \"Only applies to US equity strategies with an income layer.\",\n incomeLayerDefaultMeta: \"Strategy default: starts at {start}, max {ratio}.\",\n incomeLayerAllocationMeta: \"Default allocation: {allocations}.\",\n incomeLayerStartMeta: \"Income layer activates after total assets reach this amount.\",\n incomeLayerRatioMeta: \"Use 0.55 for a 55% cap.\",\n optionOverlayMode: \"Option layer\",\n optionOverlaySectionTitle: \"Option layer\",\n optionOverlayCurrent: \"Keep current config\",\n optionOverlayEnabled: \"Enable option layer\",\n optionOverlayDisabled: \"Disable option layer\",\n optionOverlayNotSupported: \"No option layer for this strategy\",\n optionOverlayModeMeta: \"Enabled mode uses the strategy's default recipe and budget; ratios are not edited here.\",\n optionOverlayDefaultMeta: \"{defaults}\",\n optionOverlayFamilyGrowth: \"Growth\",\n optionOverlayFamilyIncome: \"Income\",\n optionOverlayBudgetRatio: \"budget {ratio}\",\n optionOverlayRiskRatio: \"risk {ratio}\",\n cashOnlyExecutionMode: \"Allow margin\",\n cashOnlyExecutionCurrent: \"Keep current config\",\n cashOnlyExecutionYes: \"Yes\",\n cashOnlyExecutionNo: \"No\",\n cashOnlyExecutionModeMeta: \"Choose No to use available cash only and avoid margin buying power.\",\n cashOnlyExecutionValueYes: \"Yes\",\n cashOnlyExecutionValueNo: \"No\",\n currentCashOnlyExecution: \"Current allow margin\",\n pendingCashOnlyExecution: \"Pending allow margin\",\n executionCashPolicyTitle: \"Capital boundary\",\n executionCashPolicyNote: \"Allow margin and reserve-cash overrides cannot both apply. Yes clears reserve overrides; reserve overrides force No.\",\n executionCashMarginBlocksReserve: \"Allow margin is selected; submitting will clear reserve-cash overrides.\",\n executionCashReserveBlocksMargin: \"Reserve-cash override is active; submitting will force allow margin to No.\",\n qmtPlatformCashNote: \"QMT A-share does not use margin or platform reserve cash; cash constraints live in strategy execution_cash_reserve_ratio.\",\n qmtDryRunOnlyNote: \"This platform supports no-order dry runs only; no live execution route is configured.\",\n binancePlatformNote: \"Binance does not use broker-level income/option layers; features are implemented inside strategies.\",\n invalidExecutionCashPolicyNote: \"Allow margin and reserve-cash overrides conflict. Keep only one constraint.\",\n dcaMode: \"DCA mode\",\n dcaSectionTitle: \"DCA\",\n dcaModeFixed: \"Fixed DCA\",\n dcaModeSmart: \"Smart DCA\",\n dcaBaseInvestmentUsd: \"Base DCA amount\",\n dcaModeMeta: \"Only DCA strategies can use this.\",\n dcaDefaultMeta: \"Default: {mode}, base amount {amount}.\",\n dcaNotSupported: \"This is not a DCA strategy\",\n dcaPlatformNotSupported: \"DCA not supported on this platform\",\n currentDca: \"Current DCA settings\",\n pendingDca: \"Pending DCA settings\",\n dcaText: \"{mode}, base amount {amount}\",\n minReservedCash: \"Minimum reserved cash ({currency})\",\n reservedCashRatio: \"Reserved cash ratio\",\n reservedCashMode: \"Reserved cash policy\",\n reservePolicyCurrent: \"Keep current config\",\n reservePolicyNone: \"No platform reserve\",\n reservePolicyRatio: \"Ratio only\",\n reservePolicyFloor: \"Fixed amount only\",\n reservePolicyMax: \"Max of amount and ratio\",\n reservedCashModeMeta: \"Choose whether to keep, clear, or override platform reserved cash.\",\n reservedCashNone: \"None\",\n reservedCashDefault: \"Not configured (platform default: 0 {currency} / 0%)\",\n reservedCashMeta: \"Fixed cash floor. Use alone or with a ratio.\",\n reservedCashRatioMeta: \"Use 0.03 for 3%.\",\n summary: \"Risk and change summary\",\n summaryCurrent: \"Current boundary\",\n summaryPending: \"Pending change\",\n planReadinessTitle: \"Before submission\",\n planCheckAccount: \"Account and scope\",\n planCheckStrategy: \"Strategy and environment\",\n planCheckRisk: \"Cash and risk boundary\",\n planCheckAuthority: \"Live execution authority\",\n planCheckWaiting: \"Waiting\",\n planCheckSelected: \"Read\",\n planCheckValid: \"Checked\",\n planCheckFix: \"Needs correction\",\n planCheckNonLive: \"Non-live\",\n planCheckNoAuthority: \"Not granted\",\n planAuditNote: \"A submission creates an audit record; it is not an order, funding, or live permission.\",\n copySummary: \"Copy state\",\n loginToRun: \"Sign in to submit a plan\",\n loadingConfig: \"Loading config\",\n configureAccounts: \"Configure accounts\",\n runSwitch: \"Submit bounded change\",\n noChanges: \"No changes\",\n readonlyNote: \"Sign in to submit a bounded configuration.\",\n publicReadonly: \"Sign in to view account config.\",\n loadingConfigNote: \"Reading account config and current state.\",\n missingConfigNote: \"Account config is not loaded, so switching is disabled.\",\n readyNote: \"This submits a bounded configuration change; it grants no order, funding, or live authority.\",\n invalidStrategyNote: \"This account has no runnable strategy, so switching is disabled.\",\n invalidReservePolicyNote: \"Enter a valid amount or ratio for the selected reserved-cash policy.\",\n invalidIncomeLayerNote: \"Enter a valid income layer start amount and max ratio.\",\n invalidOptionOverlayNote: \"This strategy does not define an option layer to enable.\",\n invalidDcaNote: \"Enter a valid DCA mode and base amount.\",\n publicOauthTitle: \"Protected by GitHub OAuth\",\n publicOauthText: \"Only allowlisted accounts can open private config.\",\n publicWorkerTitle: \"Worker-side dispatch\",\n publicWorkerText: \"Tokens stay server-side; the browser submits intent only.\",\n publicAuditTitle: \"Traceable changes\",\n publicAuditText: \"Bounded configuration changes run through GitHub Actions for audit and rollback.\",\n noAccount: \"No accounts\",\n noStrategy: \"No supported strategies\",\n repository: \"Repository\",\n selectedAccount: \"Account\",\n selectedMarket: \"Market\",\n currentRuntimeTarget: \"Current account status\",\n pendingRuntimeTarget: \"Pending account status\",\n reservedCashPolicy: \"Current reserved cash\",\n currentIncomeLayer: \"Current income layer\",\n pendingIncomeLayer: \"Pending income layer\",\n currentOptionOverlay: \"Current option layer\",\n pendingOptionOverlay: \"Pending option layer\",\n pendingReservedCashPolicy: \"Pending reserved cash\",\n pendingMode: \"Pending mode\",\n currentPluginMode: \"Current plugin scope\",\n pendingPluginMode: \"Pending plugin scope\",\n unchanged: \"Unchanged\",\n copied: \"State copied\",\n dispatching: \"Dispatching workflow...\",\n dispatched: \"Workflow dispatched\",\n dispatchFailed: \"Dispatch failed\",\n targetMeta: \"target {target} · service {service} · market {domains}\",\n strategyMeta: \"Markets: {domains}\",\n strategyLifecycleMeta: \"current gate {stage}\",\n strategyBlockedCountMeta: \"{count} strategies are blocked from live\",\n strategyDefaultBlockedMeta: \"Default strategy {profile} is blocked: {reason}\",\n usEquity: \"US equity\",\n hkEquity: \"HK equity\",\n cnEquity: \"CN A-share\",\n cryptoEquity: \"Crypto\",\n currentStrategy: \"Current strategy\",\n nextStrategy: \"Switch strategy\",\n notRead: \"Not read\",\n runtimeTargetOn: \"Enabled\",\n runtimeTargetOff: \"Disabled\",\n incomeLayerDefault: \"Enabled, {start} start, {ratio} max\",\n incomeLayerOff: \"Disabled\",\n incomeLayerOn: \"Enabled, starts at {start}, max {ratio}\",\n optionOverlayOff: \"Disabled\",\n optionOverlayOn: \"Enabled\",\n optionOverlayDefaultSimple: \"Strategy default: enabled\",\n optionOverlayDefault: \"Enabled, {detail}\",\n cashOnlyExecutionDefault: \"Cash only\",\n },\n };\n\n const storedLang = localStorage.getItem(\"qsl-switch-lang\");\n const initialLang = storedLang === \"zh\" || storedLang === \"en\"\n ? storedLang\n : ((navigator.language || \"\").toLowerCase().startsWith(\"zh\") ? \"zh\" : \"en\");\n const clone = (value) => JSON.parse(JSON.stringify(value));\n const defaultReserveForm = () => ({\n reservePolicyMode: \"current\",\n minReservedCashUsd: \"\",\n reservedCashRatio: \"\",\n reservedCashTouched: false,\n incomeLayerMode: \"current\",\n incomeLayerStartUsd: \"\",\n incomeLayerMaxRatio: \"\",\n incomeLayerTouched: false,\n optionOverlayMode: \"current\",\n optionOverlayTouched: false,\n cashOnlyExecutionMode: \"current\",\n cashOnlyExecutionTouched: false,\n runtimeTargetMode: \"current\",\n runtimeTargetTouched: false,\n dcaMode: \"fixed\",\n dcaBaseInvestmentUsd: \"\",\n dcaTouched: false,\n strategyTouched: false,\n });\n\n const state = {\n selected: \"longbridge\",\n lang: initialLang,\n view: \"control\",\n appReady: false,\n bootMessageKey: \"bootMessage\",\n auth: { available: false, allowed: false, admin: false, login: null },\n accountOptions: clone(defaultAccountOptions),\n currentStrategies: {},\n health: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { strategy_count: 0, healthy: 0, watch: 0, review: 0, critical: 0 },\n strategies: [],\n },\n filter: \"all\",\n },\n controlPlane: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { candidate_count: 0, deferred: 0, parked: 0, owner_decision_required: 0 },\n attention: { status: \"unavailable\", reason_codes: [\"control_plane_source_unavailable\"] },\n candidates: [],\n policy: { p4_p5_automation: \"not_configured\", p6_owner_decision_required: true },\n errors: [],\n },\n },\n ownerDecisions: {\n data_status: \"unavailable\",\n candidates: [],\n errors: [],\n },\n m0Research: {\n payload: {\n data_status: \"unavailable\",\n viewed_at: null,\n summary: {\n subject_count: 0,\n observation_count: 0,\n fresh_observation_count: 0,\n stale_observation_count: 0,\n unknown_observation_count: 0,\n horizon_conflict_count: 0,\n historical_stale_horizon_drift_count: 0,\n },\n subjects: [],\n policy: { authority: \"research_only\", no_order: true, permitted_next_step: \"research_validation_only\" },\n errors: [],\n },\n },\n adaptiveSelection: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { source_count: 0, decision_count: 0, candidate_count: 0, recommended_count: 0, rejected_candidate_count: 0 },\n selections: [],\n policy: { authority: \"shadow_only\", no_order: true, execution_authority_granted: false },\n errors: [],\n },\n },\n executionEvidence: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { deployment_count: 0, autonomous_shadow: 0, autonomous_paper: 0, owner_canary_decision: 0, parked: 0 },\n deployments: [],\n policy: { execution_evidence_read_only: true, p6_owner_decision_required: true, limited_live_canary_active: false },\n errors: [],\n },\n },\n researchTasks: {\n payload: {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { task_count: 0 },\n tasks: [],\n policy: { research_only: true, no_order: true, size_zero_required: true, p4_p5_p6_authorized: false },\n errors: [],\n },\n },\n configSource: \"default\",\n repositories: clone(defaultRepositories),\n forms: {\n longbridge: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n ibkr: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n schwab: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n firstrade: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\", ...defaultReserveForm() },\n qmt: { accountKey: \"preview\", strategy: \"\", executionMode: \"paper\", pluginMode: \"none\", ...defaultReserveForm() },\n binance: { accountKey: \"preview\", strategy: \"\", executionMode: \"live\", pluginMode: \"none\" },\n },\n };\n\n const el = (id) => document.getElementById(id);\n const t = (key) => copy[state.lang][key] || copy.en[key] || key;\n let toastTimer = null;\n\n function showToast(message, { duration = 4000 } = {}) {\n const node = el(\"toast\");\n if (toastTimer) {\n window.clearTimeout(toastTimer);\n toastTimer = null;\n }\n node.textContent = message || \"\";\n if (message && duration > 0) {\n toastTimer = window.setTimeout(() => {\n node.textContent = \"\";\n toastTimer = null;\n }, duration);\n }\n }\n\n async function fetchWithTimeout(url, init = {}, timeoutMs = APP_BOOT_TIMEOUT_MS) {\n const controller = new AbortController();\n const timeoutId = window.setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (error) {\n if (error?.name === \"AbortError\") {\n throw new Error(\"request timeout\");\n }\n throw error;\n } finally {\n window.clearTimeout(timeoutId);\n }\n }\n\n async function requestJson(url, init = {}, timeoutMs = APP_BOOT_TIMEOUT_MS) {\n const response = await fetchWithTimeout(url, { ...init, cache: \"no-store\" }, timeoutMs);\n if (!response.ok) throw new Error(\"request failed\");\n return response.json();\n }\n\n function isRequestTimeoutError(error) {\n return String(error?.message || \"\").toLowerCase() === \"request timeout\";\n }\n\n function optionsFor(platform) {\n return state.accountOptions[platform] && state.accountOptions[platform].length\n ? state.accountOptions[platform]\n : defaultAccountOptions[platform];\n }\n\n function selectedAccount(platform = state.selected) {\n const options = optionsFor(platform);\n const form = state.forms[platform];\n return options.find((option) => option.key === form.accountKey) || options[0];\n }\n\n function hasPrivateConfig() {\n return Boolean(state.auth.allowed && state.configSource === \"private\");\n }\n\n function cleanStrategyProfile(value) {\n const profile = String(value || \"\").trim();\n return /^[a-z0-9._=-]{1,120}$/.test(profile) ? profile : \"\";\n }\n\n function cleanStrategyDomain(value) {\n const domain = String(value || \"\").trim();\n return strategyDomains.includes(domain) ? domain : \"\";\n }\n\n function domainLabel(domain) {\n const entry = domainLabels[domain];\n if (entry) return state.lang === \"zh\" ? entry.zh : entry.en;\n return domain;\n }\n\n function platformSupportsMarginPolicy(platform = state.selected) {\n return platformConfig[platform]?.margin_policy ?? true;\n }\n\n function platformSupportsReservedCashPolicy(platform = state.selected) {\n return platformConfig[platform]?.reserved_cash ?? true;\n }\n\n function platformDryRunOnly(platform = state.selected) {\n return platformConfig[platform]?.dry_run_only ?? false;\n }\n\n function supportedExecutionModesForPlatform(platform = state.selected) {\n const modes = platformConfig[platform]?.supported_execution_modes;\n return Array.isArray(modes) ? modes.filter((mode) => mode === \"live\" || mode === \"dry_run\") : [];\n }\n\n function allowMarginExplicitlySelected(form) {\n return normalizeCashOnlyExecutionMode(form?.cashOnlyExecutionMode) === \"disabled\";\n }\n\n function reserveCashOverrideActive(form) {\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n return mode === \"ratio\" || mode === \"floor\" || mode === \"max\";\n }\n\n function executionCashPolicyConflict(form) {\n return allowMarginExplicitlySelected(form) && reserveCashOverrideActive(form);\n }\n\n function reconcileExecutionCashPolicy(form, changed) {\n if (!form) return;\n if (changed === \"margin\" && allowMarginExplicitlySelected(form)) {\n if (form.reservePolicyMode !== \"none\" && (form.minReservedCashUsd || form.reservedCashRatio)) {\n form._prevReserve = {\n mode: form.reservePolicyMode,\n floor: form.minReservedCashUsd,\n ratio: form.reservedCashRatio,\n };\n }\n form.reservePolicyMode = \"none\";\n form.reservedCashTouched = true;\n } else if (changed === \"reserve\" && reserveCashOverrideActive(form)) {\n form.cashOnlyExecutionMode = \"enabled\";\n form.cashOnlyExecutionTouched = true;\n }\n }\n\n function restoreReserveAfterMarginDisabled(form) {\n if (!form || allowMarginExplicitlySelected(form) || !form._prevReserve) return;\n form.reservePolicyMode = form._prevReserve.mode;\n form.minReservedCashUsd = form._prevReserve.floor;\n form.reservedCashRatio = form._prevReserve.ratio;\n form.reservedCashTouched = true;\n delete form._prevReserve;\n }\n\n function strategyDomain(profile) {\n return strategyCatalog[profile]?.domain || \"\";\n }\n\n function selectedCashCurrency(platform = state.selected, account = selectedAccount(platform)) {\n const configured = String(account?.cash_currency || \"\").trim().toUpperCase();\n if (configured === \"USD\" || configured === \"HKD\" || configured === \"CNY\") return configured;\n const domain = strategyDomain(state.forms[platform]?.strategy);\n if (domain === \"hk_equity\") return \"HKD\";\n if (domain === \"cn_equity\") return \"CNY\";\n return \"USD\";\n }\n\n function applyStrategyProfiles(rawProfiles) {\n const profiles = Array.isArray(rawProfiles) && rawProfiles.length\n ? rawProfiles\n : defaultStrategyProfiles;\n const nextOptions = [];\n const nextLabels = {};\n const nextCatalog = {};\n const nextIncomeLayerDefaults = {};\n const nextOptionOverlayDefaults = {};\n for (const item of profiles) {\n const profile = cleanStrategyProfile(item?.profile || item?.strategy_profile);\n if (!profile || nextOptions.includes(profile)) continue;\n const domain = cleanStrategyDomain(item?.domain || \"us_equity\");\n if (!domain) continue;\n nextOptions.push(profile);\n nextLabels[profile] = strategyLabelSet(profile, item);\n nextCatalog[profile] = {\n profile,\n label: nextLabels[profile].en || nextLabels[profile].zh || profile,\n label_en: nextLabels[profile].en || \"\",\n label_zh: nextLabels[profile].zh || \"\",\n domain,\n runtime_enabled: cleanOptionalBoolean(item?.runtime_enabled ?? false) === true,\n };\n const lifecycleStage = normalizeLifecycleStage(item?.lifecycle_stage ?? item?.lifecycleStage);\n if (lifecycleStage) nextCatalog[profile].lifecycle_stage = lifecycleStage;\n const allowedExecutionModes = normalizeAllowedExecutionModes(item?.allowed_execution_modes);\n if (allowedExecutionModes.length) nextCatalog[profile].allowed_execution_modes = allowedExecutionModes;\n const canSwitchLive = cleanOptionalBoolean(item?.can_switch_live);\n if (canSwitchLive !== null) nextCatalog[profile].can_switch_live = canSwitchLive;\n const blockedLiveReason = cleanDisplayText(item?.blocked_live_reason);\n if (blockedLiveReason) nextCatalog[profile].blocked_live_reason = blockedLiveReason;\n const latestEvidenceStatus = cleanDisplayText(item?.latest_evidence_status);\n if (latestEvidenceStatus) nextCatalog[profile].latest_evidence_status = latestEvidenceStatus;\n const pluginGateStatus = cleanDisplayText(item?.plugin_gate_status);\n if (pluginGateStatus) nextCatalog[profile].plugin_gate_status = pluginGateStatus;\n const dcaDefaults = dcaProfileDefaults[profile] || null;\n if (dcaDefaults || item?.dca_enabled === true) {\n nextCatalog[profile].dca_enabled = true;\n nextCatalog[profile].dca_default_mode = normalizeDcaMode(\n item?.dca_default_mode || item?.default_dca_mode || dcaDefaults?.defaultMode || \"fixed\",\n );\n nextCatalog[profile].dca_default_base_investment_usd = cleanDisplayPositiveNumber(\n item?.dca_default_base_investment_usd ||\n item?.default_dca_base_investment_usd ||\n dcaDefaults?.defaultBaseInvestmentUsd ||\n \"1000\",\n ) || \"1000\";\n }\n const profileIncomeDefaults = incomeLayerDefaultsFromProfileItem(item);\n const incomeDefaults = profileIncomeDefaults === false\n ? null\n : (profileIncomeDefaults || fallbackIncomeLayerDefaults[profile] || null);\n if (incomeDefaults) {\n nextIncomeLayerDefaults[profile] = incomeDefaults;\n nextCatalog[profile].income_layer_enabled = true;\n nextCatalog[profile].income_layer_start_usd = String(incomeDefaults.startUsd);\n nextCatalog[profile].income_layer_max_ratio = incomeDefaults.maxRatio;\n nextCatalog[profile].income_layer_allocations = incomeDefaults.allocations;\n }\n const profileOptionDefaults = optionOverlayDefaultsFromProfileItem(item);\n const optionDefaults = profileOptionDefaults === false\n ? null\n : (profileOptionDefaults || fallbackOptionOverlayDefaults[profile] || null);\n if (optionDefaults) {\n nextOptionOverlayDefaults[profile] = optionDefaults;\n nextCatalog[profile].option_overlay_enabled = true;\n nextCatalog[profile].option_overlay_live_gate = optionDefaults.liveGate || \"\";\n nextCatalog[profile].option_overlay_live_status = optionDefaults.liveStatus || \"\";\n }\n }\n if (!nextOptions.length && profiles !== defaultStrategyProfiles) return applyStrategyProfiles(defaultStrategyProfiles);\n strategyOptions = nextOptions;\n strategyLabels = nextLabels;\n strategyCatalog = nextCatalog;\n incomeLayerDefaults = nextIncomeLayerDefaults;\n optionOverlayDefaults = nextOptionOverlayDefaults;\n }\n\n function incomeLayerDefaultsFromProfileItem(item) {\n const enabled = cleanOptionalBoolean(item?.income_layer_enabled);\n const hasConfig = enabled !== null ||\n item?.income_layer_start_usd !== undefined ||\n item?.income_layer_max_ratio !== undefined ||\n item?.income_layer_allocations !== undefined;\n if (!hasConfig) return null;\n if (enabled === false) return false;\n const startUsd = cleanDisplayNumber(item?.income_layer_start_usd);\n const maxRatio = cleanDisplayRatio(item?.income_layer_max_ratio);\n const allocations = cleanIncomeLayerAllocations(item?.income_layer_allocations);\n if (!startUsd || !maxRatio || !allocations) return null;\n return { startUsd, maxRatio, allocations };\n }\n\n function cleanIncomeLayerAllocations(value) {\n if (!value || Array.isArray(value) || typeof value !== \"object\") return null;\n const allocations = {};\n let total = 0;\n for (const [rawSymbol, rawRatio] of Object.entries(value)) {\n const symbol = String(rawSymbol || \"\").trim().toUpperCase();\n const ratio = cleanDisplayPositiveNumber(rawRatio);\n if (!/^[A-Z0-9.-]{1,12}$/.test(symbol) || !ratio) continue;\n allocations[symbol] = Number(ratio);\n total += Number(ratio);\n }\n return total > 0 && Object.keys(allocations).length ? allocations : null;\n }\n\n function optionOverlayDefaultsFromProfileItem(item) {\n const enabled = cleanOptionalBoolean(item?.option_overlay_enabled);\n const hasConfig = enabled !== null ||\n item?.option_growth_overlay_enabled !== undefined ||\n item?.option_income_overlay_enabled !== undefined ||\n item?.option_overlay_live_gate !== undefined ||\n item?.option_overlay_live_status !== undefined;\n if (!hasConfig) return null;\n if (enabled === false) return false;\n const families = [\n optionOverlayFamilyDefaultsFromProfileItem(item, \"growth\"),\n optionOverlayFamilyDefaultsFromProfileItem(item, \"income\"),\n ].filter(Boolean);\n if (!families.length) return null;\n return {\n liveGate: String(item?.option_overlay_live_gate || \"promotion_required\"),\n liveStatus: String(item?.option_overlay_live_status || \"research_only\"),\n families,\n };\n }\n\n function optionOverlayFamilyDefaultsFromProfileItem(item, family) {\n const prefix = `option_${family}_overlay`;\n const enabled = cleanOptionalBoolean(item?.[`${prefix}_enabled`]);\n if (enabled !== true) return null;\n const recipe = cleanStrategyProfile(item?.[`${prefix}_recipe`]);\n const startUsd = cleanDisplayNumber(item?.[`${prefix}_start_usd`]);\n const ratioField = family === \"growth\"\n ? \"option_growth_overlay_nav_budget_ratio\"\n : \"option_income_overlay_nav_risk_ratio\";\n const ratio = cleanDisplayRatio(item?.[ratioField]);\n if (!recipe || !startUsd || !ratio) return null;\n return {\n family,\n recipe,\n startUsd,\n ratio,\n ratioKind: family === \"growth\" ? \"budget\" : \"risk\",\n };\n }\n\n function supportedDomainsForAccount(platform, account) {\n if (Array.isArray(account?.supported_domains) && account.supported_domains.length) {\n const cleaned = account.supported_domains.map(cleanStrategyDomain).filter(Boolean);\n if (cleaned.length) return [...new Set(cleaned)];\n }\n return inferSupportedDomains(platform, account);\n }\n\n function inferSupportedDomains(platform, account) {\n void account;\n if (platform === \"qmt\") return [\"cn_equity\"];\n if (platform === \"longbridge\" || platform === \"ibkr\") return [\"us_equity\", \"hk_equity\"];\n return [\"us_equity\"];\n }\n\n function supportedDomainLabel(platform, account) {\n return supportedDomainsForAccount(platform, account).map(domainLabel).join(\" / \");\n }\n\n function platformSupportsDca(platform = state.selected) {\n return platformConfig[platform]?.dca ?? false;\n }\n\n function strategyCatalogEntry(profile) {\n return strategyCatalog[cleanStrategyProfile(profile)] || {};\n }\n\n function normalizeLifecycleStage(value) {\n const text = String(value || \"\").trim().toLowerCase();\n if (!text || text.length > 40 || !/^[a-z0-9._-]+$/.test(text)) return \"\";\n if ([\"research_backtest_only\", \"ai_monitored_candidate\"].includes(text)) return \"research_active\";\n if (text === \"shadow_candidate\") return \"shadow_active\";\n if (text === \"runtime_enabled\") return \"live_candidate\";\n return text;\n }\n\n function normalizeAllowedExecutionModes(value) {\n const items = Array.isArray(value)\n ? value\n : String(value || \"\").split(/[,\\s/|]+/);\n const modes = [];\n for (const item of items) {\n const mode = String(item || \"\").trim().toLowerCase();\n if (!mode || mode.length > 32 || !/^[a-z0-9._-]+$/.test(mode)) continue;\n if (!modes.includes(mode)) modes.push(mode);\n }\n return modes;\n }\n\n function cleanDisplayText(value) {\n const text = String(value ?? \"\").trim();\n if (!text || text.length > 120 || /[<>]/.test(text)) return \"\";\n return text;\n }\n\n function strategyCanSwitchLive(entry) {\n if (!entry || typeof entry !== \"object\") return false;\n if (entry.runtime_enabled !== true) return false;\n const allowedModes = normalizeAllowedExecutionModes(entry.allowed_execution_modes);\n if (!allowedModes.includes(\"live\")) return false;\n if (cleanOptionalBoolean(entry.can_switch_live) !== true) return false;\n const lifecycleStage = normalizeLifecycleStage(entry.lifecycle_stage);\n if (![\"live_enabled\", \"runtime_enabled\"].includes(lifecycleStage)) return false;\n const blockedReason = cleanDisplayText(entry.blocked_live_reason);\n if (blockedReason) return false;\n const evidenceStatus = cleanDisplayText(entry.latest_evidence_status);\n if (evidenceStatus && [\"research_only\", \"blocked\", \"pending\"].includes(evidenceStatus.toLowerCase())) return false;\n const pluginGateStatus = cleanDisplayText(entry.plugin_gate_status);\n if (pluginGateStatus && [\"blocked\", \"locked\", \"disabled\"].includes(pluginGateStatus.toLowerCase())) return false;\n return true;\n }\n\n function strategyDisplayMetaText(platform, account, profile) {\n void profile;\n return t(\"strategyMeta\").replace(\"{domains}\", supportedDomainLabel(platform, account));\n }\n\n function strategyActionNoteText(platform = state.selected, account = selectedAccount(platform)) {\n const profile = state.forms[platform]?.strategy || \"\";\n const meta = strategyDisplayMetaText(platform, account, profile);\n return meta ? `${t(\"invalidStrategyNote\")}\\n${meta}` : t(\"invalidStrategyNote\");\n }\n\n function strategyChoiceLabel(profile, platform = state.selected, account = selectedAccount(platform), executionMode = state.forms[platform]?.executionMode) {\n const label = strategyLabel(profile);\n const entry = strategyCatalogEntry(profile);\n const domain = entry.domain ? domainLabel(entry.domain) : \"\";\n if (!entry.profile) return label;\n if (normalizeExecutionMode(executionMode, false) === \"live\" && !strategyCanSwitchLive(entry)) {\n return domain ? `${label}(${domain})` : label;\n }\n if (entry.runtime_enabled === false) {\n return domain ? `${label}(${domain})` : label;\n }\n return label;\n }\n\n function strategyAllowedForAccount(platform, account, profile, executionMode = state.forms[platform]?.executionMode) {\n const cleanProfile = cleanStrategyProfile(profile);\n const catalogEntry = strategyCatalogEntry(cleanProfile);\n if (!catalogEntry.profile) return false;\n if (dcaConfigForStrategy(cleanProfile) && !platformSupportsDca(platform)) return false;\n if (!supportedDomainsForAccount(platform, account).includes(catalogEntry.domain)) return false;\n const mode = normalizeExecutionMode(executionMode, false);\n if (!supportedExecutionModesForPlatform(platform).includes(mode)) return false;\n if (mode === \"live\") return strategyCanSwitchLive(catalogEntry);\n const allowedModes = normalizeAllowedExecutionModes(catalogEntry.allowed_execution_modes);\n if (allowedModes.length && !allowedModes.includes(mode)) return false;\n return true;\n }\n\n function strategyChoicesForAccount(platform = state.selected, account = selectedAccount(platform), executionMode = state.forms[platform]?.executionMode) {\n const choices = strategyOptions.filter((profile) => strategyAllowedForAccount(platform, account, profile, executionMode));\n const addChoice = (value) => {\n const profile = cleanStrategyProfile(value);\n if (profile && !choices.includes(profile) && strategyAllowedForAccount(platform, account, profile, executionMode)) {\n choices.push(profile);\n }\n };\n return choices;\n }\n\n function hasLiveStrategyOption(platform = state.selected, account = selectedAccount(platform)) {\n return strategyOptions.some((profile) => strategyAllowedForAccount(platform, account, profile, \"live\"));\n }\n\n function strategyLabel(profile) {\n const labels = strategyLabels[profile] || localStrategyLabels[profile];\n if (!labels) return profile;\n return state.lang === \"zh\"\n ? (labels.zh || labels.en || profile)\n : (labels.en || labels.zh || profile);\n }\n\n function strategyLabelSet(profile, item) {\n const local = localStrategyLabels[profile] || {};\n const label = String(item?.label || item?.display_name || \"\").trim();\n const labelEn = String(item?.label_en || item?.display_name_en || \"\").trim();\n const labelZh = String(item?.label_zh || item?.display_name_zh || \"\").trim();\n return {\n zh: labelZh || local.zh || label || local.en || profile,\n en: labelEn || label || local.en || labelZh || local.zh || profile,\n };\n }\n\n function modeLabel(mode) {\n return mode === \"dry_run\" ? t(\"dryRun\") : mode === \"paper\" ? t(\"paper\") : t(\"live\");\n }\n\n function normalizePluginMode(value) {\n return pluginModes.includes(value) ? value : \"none\";\n }\n\n function pluginModeLabel(mode) {\n void mode;\n return t(\"pluginModeNone\");\n }\n\n function dcaConfigForStrategy(profile) {\n const cleanProfile = cleanStrategyProfile(profile);\n const catalog = strategyCatalog[cleanProfile] || {};\n if (catalog.dca_enabled === true) {\n return {\n defaultMode: normalizeDcaMode(catalog.dca_default_mode || \"fixed\"),\n defaultBaseInvestmentUsd: cleanDisplayPositiveNumber(catalog.dca_default_base_investment_usd) || \"1000\",\n };\n }\n return dcaProfileDefaults[cleanProfile] || null;\n }\n\n function dcaSupported(profile) {\n return Boolean(dcaConfigForStrategy(profile));\n }\n\n function normalizeDcaMode(value) {\n const mode = String(value || \"\").trim().toLowerCase();\n if (mode === \"smart_dca\") return \"smart\";\n if (mode === \"fixed_dca\" || mode === \"ordinary\" || mode === \"ordinary_dca\") return \"fixed\";\n return dcaModes.includes(mode) ? mode : \"fixed\";\n }\n\n function dcaModeLabel(mode) {\n return normalizeDcaMode(mode) === \"smart\" ? t(\"dcaModeSmart\") : t(\"dcaModeFixed\");\n }\n\n function normalizeRuntimeTargetMode(value) {\n return runtimeTargetModes.includes(value) ? value : \"current\";\n }\n\n function runtimeTargetModeLabel(mode) {\n if (mode === \"enabled\") return t(\"runtimeTargetEnabled\");\n if (mode === \"disabled\") return t(\"runtimeTargetDisabled\");\n return t(\"runtimeTargetCurrent\");\n }\n\n function runtimeTargetEnabledForAccount(platform, account) {\n return cleanOptionalBoolean(currentEntryForAccount(platform, account)?.runtime_target_enabled);\n }\n\n function runtimeTargetStateForAccount(platform = state.selected, account = selectedAccount(platform)) {\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return { known: false, enabled: null };\n const configured = cleanOptionalBoolean(entry.runtime_target_enabled);\n return { known: true, enabled: configured ?? true };\n }\n\n function runtimeTargetText(enabled) {\n return enabled ? t(\"runtimeTargetOn\") : t(\"runtimeTargetOff\");\n }\n\n function runtimeTargetTone(enabled) {\n return enabled ? \"enabled\" : \"disabled\";\n }\n\n function currentRuntimeTargetText(platform = state.selected, account = selectedAccount(platform)) {\n const target = runtimeTargetStateForAccount(platform, account);\n if (!target.known) return t(\"notRead\");\n return runtimeTargetText(target.enabled);\n }\n\n function currentRuntimeTargetTone(platform = state.selected, account = selectedAccount(platform)) {\n const target = runtimeTargetStateForAccount(platform, account);\n if (!target.known) return \"neutral\";\n return runtimeTargetTone(target.enabled);\n }\n\n function incomeLayerDefaultForStrategy(profile) {\n return incomeLayerDefaults[cleanStrategyProfile(profile)] || null;\n }\n\n function incomeLayerSupported(profile) {\n return Boolean(incomeLayerDefaultForStrategy(profile));\n }\n\n function normalizeIncomeLayerMode(value) {\n return incomeLayerModes.includes(value) ? value : \"current\";\n }\n\n function incomeLayerModeLabel(mode) {\n if (mode === \"enabled\") return t(\"incomeLayerEnabled\");\n if (mode === \"disabled\") return t(\"incomeLayerDisabled\");\n return t(\"incomeLayerCurrent\");\n }\n\n function optionOverlayDefaultForStrategy(profile) {\n return optionOverlayDefaults[cleanStrategyProfile(profile)] || null;\n }\n\n function optionOverlaySupported(profile) {\n return Boolean(optionOverlayDefaultForStrategy(profile));\n }\n\n function normalizeOptionOverlayMode(value) {\n return optionOverlayModes.includes(value) ? value : \"current\";\n }\n\n function optionOverlayModeLabel(mode) {\n if (mode === \"enabled\") return t(\"optionOverlayEnabled\");\n if (mode === \"disabled\") return t(\"optionOverlayDisabled\");\n return t(\"optionOverlayCurrent\");\n }\n\n function optionOverlayText(enabled) {\n return enabled ? t(\"optionOverlayOn\") : t(\"optionOverlayOff\");\n }\n\n function normalizeCashOnlyExecutionMode(value) {\n return cashOnlyExecutionModes.includes(value) ? value : \"current\";\n }\n\n function cashOnlyExecutionModeLabel(mode) {\n if (mode === \"enabled\") return t(\"cashOnlyExecutionNo\");\n if (mode === \"disabled\") return t(\"cashOnlyExecutionYes\");\n return t(\"cashOnlyExecutionCurrent\");\n }\n\n function cashOnlyExecutionText(enabled) {\n if (enabled === null) return t(\"notRead\");\n return enabled ? t(\"cashOnlyExecutionValueNo\") : t(\"cashOnlyExecutionValueYes\");\n }\n\n function platformCashOnlyExecutionDefault() {\n return true;\n }\n\n function effectiveCashOnlyExecutionForAccount(platform, account) {\n const configured = currentCashOnlyExecutionForAccount(platform, account);\n if (configured !== null) return configured;\n if (!platformSupportsMarginPolicy(platform)) return null;\n return platformCashOnlyExecutionDefault();\n }\n\n function currentCashOnlyExecutionForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n if (entry) {\n const val = cleanOptionalBoolean(entry.cash_only_execution);\n if (val !== null) return val;\n }\n return platformCashOnlyExecutionDefault();\n }\n\n function currentCashOnlyExecutionText(platform = state.selected, account = selectedAccount(platform)) {\n if (!platformSupportsMarginPolicy(platform)) return t(\"notRead\");\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const configured = cleanOptionalBoolean(entry.cash_only_execution);\n if (configured === null) return t(\"cashOnlyExecutionDefault\");\n return cashOnlyExecutionText(configured);\n }\n\n function currentOptionOverlayForAccount(platform, account) {\n return cleanOptionalBoolean(currentEntryForAccount(platform, account)?.option_overlay_enabled);\n }\n\n function effectiveOptionOverlayForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const configured = currentOptionOverlayForAccount(platform, account);\n if (configured !== null) return configured;\n if (!optionOverlaySupported(profile)) return null;\n return true;\n }\n\n function optionOverlayDefaultSummaryDetail(defaults) {\n if (!defaults?.families?.length) return \"\";\n return defaults.families.map((item) => {\n const family = item.family === \"income\" ? t(\"optionOverlayFamilyIncome\") : t(\"optionOverlayFamilyGrowth\");\n const ratioText = item.ratioKind === \"risk\"\n ? t(\"optionOverlayRiskRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio))\n : t(\"optionOverlayBudgetRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio));\n return `${family} ${ratioText}`;\n }).join(\" / \");\n }\n\n function optionOverlayDefaultText(profile) {\n const defaults = optionOverlayDefaultForStrategy(profile);\n if (!defaults) return t(\"optionOverlayNotSupported\");\n const detail = optionOverlayDefaultSummaryDetail(defaults);\n return detail ? t(\"optionOverlayDefault\").replace(\"{detail}\", detail) : t(\"optionOverlayDefaultSimple\");\n }\n\n function currentOptionOverlayText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const configured = cleanOptionalBoolean(entry.option_overlay_enabled);\n if (!optionOverlaySupported(profile)) {\n return configured === null ? t(\"optionOverlayNotSupported\") : optionOverlayText(configured);\n }\n if (configured === null) return optionOverlayDefaultText(profile);\n return optionOverlayText(configured);\n }\n\n function currentIncomeLayerForAccount(platform, account) {\n return incomeLayerFromEntry(currentEntryForAccount(platform, account));\n }\n\n function incomeLayerFromEntry(entry) {\n return {\n enabled: cleanOptionalBoolean(entry?.income_layer_enabled),\n startUsd: cleanDisplayNumber(entry?.income_layer_start_usd),\n maxRatio: cleanDisplayRatio(entry?.income_layer_max_ratio),\n };\n }\n\n function incomeLayerFieldsConfigured(entry) {\n const current = incomeLayerFromEntry(entry);\n return current.enabled !== null || Boolean(current.startUsd || current.maxRatio);\n }\n\n function effectiveIncomeLayerForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return null;\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return null;\n const current = incomeLayerFromEntry(entry);\n if (!incomeLayerFieldsConfigured(entry)) {\n return {\n enabled: true,\n startUsd: String(defaults.startUsd),\n maxRatio: defaults.maxRatio,\n };\n }\n return {\n enabled: current.enabled ?? true,\n startUsd: current.startUsd || String(defaults.startUsd),\n maxRatio: current.maxRatio || defaults.maxRatio,\n };\n }\n\n function currentDcaForAccount(platform, account, profile = state.forms[platform]?.strategy) {\n const defaults = dcaConfigForStrategy(profile);\n if (!defaults) return { supported: false, mode: \"\", baseInvestmentUsd: \"\" };\n const entry = currentEntryForAccount(platform, account);\n return {\n supported: true,\n mode: normalizeDcaMode(entry?.dca_mode || account?.dca_mode || defaults.defaultMode),\n baseInvestmentUsd: cleanDisplayPositiveNumber(entry?.dca_base_investment_usd) ||\n cleanDisplayPositiveNumber(account?.dca_base_investment_usd) ||\n defaults.defaultBaseInvestmentUsd,\n };\n }\n\n function normalizeAccountLookupKey(value) {\n return String(value || \"\").trim().toLowerCase();\n }\n\n function collectAccountLookupCandidates(keys) {\n const candidates = new Set();\n for (const rawKey of keys) {\n const key = normalizeAccountLookupKey(rawKey);\n if (!key) continue;\n\n candidates.add(key);\n\n const compact = key.replace(/[^a-z0-9]+/g, \"\");\n if (compact) candidates.add(compact);\n\n const parts = key.split(/[^a-z0-9]+/).filter(Boolean);\n if (parts.length > 1) candidates.add(parts[parts.length - 1]);\n }\n return [...candidates];\n }\n\n function resolveCurrentEntryByKey(byPlatform, keys) {\n const candidates = new Set(collectAccountLookupCandidates(keys));\n if (!candidates.size) return null;\n\n for (const key of keys) {\n const entry = byPlatform[key];\n if (currentEntryHasState(entry)) return entry;\n }\n\n for (const [rawKey, entry] of Object.entries(byPlatform)) {\n if (!currentEntryHasState(entry)) continue;\n const rawCandidates = collectAccountLookupCandidates([rawKey]);\n const hasMatch = rawCandidates.some((candidate) => candidates.has(candidate));\n if (hasMatch) return entry;\n }\n\n return null;\n }\n\n function currentEntryForAccount(platform, account) {\n const byPlatform = state.currentStrategies[platform] || {};\n const keys = [account?.key, account?.target_name, account?.label]\n .filter(Boolean)\n .map((value) => String(value));\n const entry = resolveCurrentEntryByKey(byPlatform, keys);\n if (entry) return entry;\n const globalDefaults = window.__DEFAULT_ACCOUNT_OPTIONS__?.[platform]?.[0] || {};\n const merged = { ...globalDefaults, ...(account || {}) };\n const synth = {\n strategy_profile: \"\",\n source: \"account_defaults\",\n };\n const cashMode = merged.cash_only_execution_mode;\n if (cashMode === \"enabled\") synth.cash_only_execution = true;\n else if (cashMode === \"disabled\") synth.cash_only_execution = false;\n else if (platformSupportsMarginPolicy(platform)) synth.cash_only_execution = true;\n if (merged.min_reserved_cash_usd) synth.min_reserved_cash_usd = merged.min_reserved_cash_usd;\n if (merged.reserved_cash_ratio) synth.reserved_cash_ratio = merged.reserved_cash_ratio;\n synth.runtime_target_enabled = merged.runtime_target_enabled !== false;\n const execMode = merged.default_execution_mode || platformConfig[platform]?.default_execution_mode || \"live\";\n synth.execution_mode = execMode;\n synth.dry_run_only = execMode === \"paper\";\n return synth;\n }\n\n function currentEntryHasState(entry) {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return false;\n return Boolean(\n cleanStrategyProfile(entry.strategy_profile) ||\n cleanDisplayNumber(entry.min_reserved_cash_usd ?? entry.reserved_cash_floor_usd) ||\n cleanDisplayRatio(entry.reserved_cash_ratio) ||\n cleanOptionalBoolean(entry.income_layer_enabled) !== null ||\n cleanDisplayNumber(entry.income_layer_start_usd) ||\n cleanDisplayRatio(entry.income_layer_max_ratio) ||\n cleanOptionalBoolean(entry.option_overlay_enabled) !== null ||\n cleanOptionalBoolean(entry.cash_only_execution) !== null ||\n cleanOptionalBoolean(entry.runtime_target_enabled) !== null ||\n normalizeDcaMode(entry.dca_mode || \"\") !== \"fixed\" ||\n cleanDisplayPositiveNumber(entry.dca_base_investment_usd) ||\n normalizeExecutionMode(entry.execution_mode, entry.dry_run_only),\n );\n }\n\n function currentStrategyForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n return cleanStrategyProfile(entry?.strategy_profile) || \"\";\n }\n\n function currentReservePolicyForAccount(platform, account) {\n const entry = currentEntryForAccount(platform, account);\n return reservePolicyFromEntry(entry);\n }\n\n function currentPluginModeForAccount(platform, account) {\n void platform;\n return normalizePluginMode(account?.plugin_mode);\n }\n\n function reservePolicyFromEntry(entry) {\n return {\n minReservedCashUsd: cleanDisplayNumber(entry?.min_reserved_cash_usd ?? entry?.reserved_cash_floor_usd),\n reservedCashRatio: cleanDisplayRatio(entry?.reserved_cash_ratio),\n };\n }\n\n function cleanDisplayNumber(value) {\n const text = String(value ?? \"\").trim();\n if (!text || text.length > 32 || !/^(?:\\d+|\\d*\\.\\d+)$/.test(text)) return \"\";\n const numeric = Number(text);\n if (!Number.isFinite(numeric) || numeric < 0) return \"\";\n return text;\n }\n\n function cleanDisplayRatio(value) {\n const text = cleanDisplayNumber(value);\n if (!text) return \"\";\n const numeric = Number(text);\n return numeric >= 0 && numeric <= 1 ? text : \"\";\n }\n\n function cleanDisplayPositiveNumber(value) {\n const text = cleanDisplayNumber(value);\n return text && Number(text) > 0 ? text : \"\";\n }\n\n function normalizeExecutionMode(value, dryRunOnly) {\n const mode = String(value || \"\").trim().toLowerCase();\n if (mode === \"live\") return \"live\";\n if (mode === \"paper\" || mode === \"dry_run\" || mode === \"dry-run\") return \"dry_run\";\n if (dryRunOnly === true || dryRunOnly === \"true\" || dryRunOnly === \"1\" || dryRunOnly === 1) return \"dry_run\";\n if (dryRunOnly === false || dryRunOnly === \"false\" || dryRunOnly === \"0\" || dryRunOnly === 0) return \"live\";\n return \"\";\n }\n\n function cleanOptionalBoolean(value) {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n if (typeof value === \"string\") {\n const text = value.trim().toLowerCase();\n if (text === \"true\" || text === \"1\") return true;\n if (text === \"false\" || text === \"0\") return false;\n }\n return null;\n }\n\n function defaultExecutionModeForAccount(platform, account, fallback = \"live\") {\n if (platformDryRunOnly(platform)) return \"dry_run\";\n const currentMode = normalizeExecutionMode(\n currentEntryForAccount(platform, account)?.execution_mode,\n currentEntryForAccount(platform, account)?.dry_run_only,\n );\n if (currentMode) return currentMode;\n const hint = [\n account?.key,\n account?.label,\n account?.target_name,\n account?.deployment_selector,\n account?.account_scope,\n account?.service_name,\n ].join(\" \").toLowerCase();\n if (hint.split(/\\s+/).includes(\"paper\") || hint.includes(\"-paper\") || hint.includes(\"_paper\") || hint.includes(\"dry_run\") || hint.includes(\"dry-run\")) {\n return \"dry_run\";\n }\n return fallback;\n }\n\n function defaultStrategyForAccount(platform, account) {\n const currentProfile = currentStrategyForAccount(platform, account);\n if (currentProfile) return currentProfile;\n return \"\";\n }\n\n function syncStrategyForAccount(platform) {\n const account = selectedAccount(platform);\n if (!account) return;\n state.forms[platform].strategy = defaultStrategyForAccount(platform, account);\n state.forms[platform].executionMode = defaultExecutionModeForAccount(\n platform,\n account,\n );\n state.forms[platform].pluginMode = currentPluginModeForAccount(platform, account);\n syncRuntimeTargetForAccount(platform);\n syncReservePolicyForAccount(platform);\n syncIncomeLayerForAccount(platform);\n syncOptionOverlayForAccount(platform);\n syncCashOnlyExecutionForAccount(platform);\n reconcileExecutionCashPolicy(state.forms[platform], \"margin\");\n syncDcaForAccount(platform);\n }\n\n function syncRuntimeTargetForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.runtimeTargetTouched) return;\n const current = runtimeTargetEnabledForAccount(platform, selectedAccount(platform));\n form.runtimeTargetMode = current === false ? \"disabled\" : \"enabled\";\n }\n\n function syncReservePolicyForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.reservedCashTouched) return;\n const policy = currentReservePolicyForAccount(platform, selectedAccount(platform));\n const hasFloor = Boolean(policy.minReservedCashUsd);\n const hasRatio = Boolean(policy.reservedCashRatio);\n if (hasFloor && hasRatio) form.reservePolicyMode = \"max\";\n else if (hasFloor) form.reservePolicyMode = \"floor\";\n else if (hasRatio) form.reservePolicyMode = \"ratio\";\n else form.reservePolicyMode = \"none\";\n form.minReservedCashUsd = policy.minReservedCashUsd;\n form.reservedCashRatio = policy.reservedCashRatio;\n }\n\n function syncIncomeLayerForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.incomeLayerTouched) return;\n const defaults = incomeLayerDefaultForStrategy(form.strategy);\n const current = currentIncomeLayerForAccount(platform, selectedAccount(platform));\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n if (entry && incomeLayerFieldsConfigured(entry)) {\n form.incomeLayerMode = current.enabled === false ? \"disabled\" : \"enabled\";\n } else {\n form.incomeLayerMode = incomeLayerSupported(form.strategy) ? \"enabled\" : \"disabled\";\n }\n form.incomeLayerStartUsd = current.startUsd || String(defaults?.startUsd || \"\");\n form.incomeLayerMaxRatio = current.maxRatio || defaults?.maxRatio || \"\";\n }\n\n function syncOptionOverlayForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.optionOverlayTouched) return;\n const configured = normalizeOptionOverlayMode(selectedAccount(platform)?.option_overlay_mode);\n if (configured !== \"current\") {\n form.optionOverlayMode = configured;\n return;\n }\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n const rawValue = cleanOptionalBoolean(entry?.option_overlay_enabled);\n if (rawValue !== null) {\n form.optionOverlayMode = rawValue ? \"enabled\" : \"disabled\";\n return;\n }\n form.optionOverlayMode = optionOverlaySupported(form.strategy) ? \"enabled\" : \"disabled\";\n }\n\n function syncCashOnlyExecutionForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.cashOnlyExecutionTouched) return;\n const configured = normalizeCashOnlyExecutionMode(selectedAccount(platform)?.cash_only_execution_mode);\n if (configured !== \"current\") {\n form.cashOnlyExecutionMode = configured;\n return;\n }\n const entry = currentEntryForAccount(platform, selectedAccount(platform));\n const rawValue = cleanOptionalBoolean(entry?.cash_only_execution);\n if (rawValue !== null) {\n form.cashOnlyExecutionMode = rawValue ? \"enabled\" : \"disabled\";\n return;\n }\n // No explicit config — use platform default (cash-only for margin-capable platforms)\n form.cashOnlyExecutionMode = platformSupportsMarginPolicy(platform) ? \"enabled\" : \"disabled\";\n }\n\n function syncDcaForAccount(platform) {\n const form = state.forms[platform];\n if (!form || form.dcaTouched) return;\n const current = currentDcaForAccount(platform, selectedAccount(platform), form.strategy);\n form.dcaMode = current.supported ? current.mode : \"fixed\";\n form.dcaBaseInvestmentUsd = current.supported ? current.baseInvestmentUsd : \"\";\n }\n\n function ensureAccountSelection(platform) {\n const options = optionsFor(platform);\n if (!options.length) return;\n if (!options.some((option) => option.key === state.forms[platform].accountKey)) {\n state.forms[platform].accountKey = options[0].key;\n state.forms[platform].runtimeTargetTouched = false;\n state.forms[platform].reservedCashTouched = false;\n state.forms[platform].incomeLayerTouched = false;\n state.forms[platform].optionOverlayTouched = false;\n state.forms[platform].cashOnlyExecutionTouched = false;\n state.forms[platform].dcaTouched = false;\n state.forms[platform].strategy = defaultStrategyForAccount(platform, options[0]);\n state.forms[platform].pluginMode = currentPluginModeForAccount(platform, options[0]);\n syncRuntimeTargetForAccount(platform);\n syncReservePolicyForAccount(platform);\n syncIncomeLayerForAccount(platform);\n syncOptionOverlayForAccount(platform);\n syncCashOnlyExecutionForAccount(platform);\n syncDcaForAccount(platform);\n }\n }\n\n function derivedService(platform, targetName) {\n if (platform === \"longbridge\") return `longbridge-quant-${targetName.toLowerCase()}-service`;\n if (platform === \"ibkr\") return `interactive-brokers-${targetName.toLowerCase()}-service`;\n if (platform === \"schwab\") return \"charles-schwab-quant-service\";\n if (platform === \"firstrade\") return \"firstrade-quant-service\";\n if (platform === \"qmt\") return \"qmt-quant-service\";\n return \"\";\n }\n\n function accountMetaText(platform = state.selected) {\n const account = selectedAccount(platform);\n const targetName = account.target_name || account.key;\n const raw = account.service_name || derivedService(platform, targetName);\n const service = raw || (state.lang === \"zh\" ? \"无\" : \"-\");\n return t(\"targetMeta\")\n .replace(\"{target}\", targetName)\n .replace(\"{service}\", service)\n .replace(\"{domains}\", supportedDomainLabel(platform, account));\n }\n\n function hasRunnableStrategySelection(platform = state.selected) {\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n return Boolean(form?.strategy && account && strategyAllowedForAccount(platform, account, form.strategy, form.executionMode));\n }\n\n function hasValidReservePolicy(platform = state.selected) {\n if (!platformSupportsReservedCashPolicy(platform)) return true;\n const form = state.forms[platform];\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n if (mode === \"current\" || mode === \"none\") return true;\n return Boolean(reservePolicyOverrideForForm(form, platform));\n }\n\n function hasValidExecutionCashPolicy(platform = state.selected) {\n if (!platformSupportsMarginPolicy(platform) && !platformSupportsReservedCashPolicy(platform)) return true;\n const form = state.forms[platform];\n return !executionCashPolicyConflict(form) && hasValidReservePolicy(platform);\n }\n\n function hasValidIncomeLayerPolicy(platform = state.selected) {\n const form = state.forms[platform];\n if (!incomeLayerSupported(form?.strategy)) return true;\n const mode = normalizeIncomeLayerMode(form?.incomeLayerMode);\n if (mode === \"current\" || mode === \"disabled\") return true;\n const defaults = incomeLayerDefaultForStrategy(form?.strategy);\n const startUsd = cleanDisplayNumber(form?.incomeLayerStartUsd || defaults?.startUsd);\n const maxRatio = cleanDisplayRatio(form?.incomeLayerMaxRatio || defaults?.maxRatio);\n return Boolean(startUsd && maxRatio);\n }\n\n function hasValidOptionOverlayPolicy(platform = state.selected) {\n const form = state.forms[platform];\n const mode = normalizeOptionOverlayMode(form?.optionOverlayMode);\n return mode !== \"enabled\" || optionOverlaySupported(form?.strategy);\n }\n\n function hasValidDcaPolicy(platform = state.selected) {\n const form = state.forms[platform];\n if (!dcaSupported(form?.strategy) || !platformSupportsDca(platform)) return true;\n return Boolean(dcaModes.includes(normalizeDcaMode(form?.dcaMode)) && cleanDisplayPositiveNumber(form?.dcaBaseInvestmentUsd));\n }\n\n function hasValidStrategySelection(platform = state.selected) {\n return hasRunnableStrategySelection(platform) &&\n hasValidExecutionCashPolicy(platform) &&\n hasValidIncomeLayerPolicy(platform) &&\n hasValidOptionOverlayPolicy(platform) &&\n hasValidDcaPolicy(platform);\n }\n\n function normalizeReservePolicyMode(value) {\n return reservePolicyModes.includes(value) ? value : \"current\";\n }\n\n function reservePolicyOverrideForForm(form, platform = state.selected) {\n if (!platformSupportsReservedCashPolicy(platform)) return null;\n const mode = normalizeReservePolicyMode(form?.reservePolicyMode);\n const floor = cleanDisplayNumber(form?.minReservedCashUsd);\n const ratio = cleanDisplayRatio(form?.reservedCashRatio);\n const extraVariables = {};\n if (mode === \"current\") return null;\n if (mode === \"none\") {\n extraVariables[platformMinReservedCashVariables[platform]] = \"\";\n extraVariables[platformReservedCashRatioVariables[platform]] = \"\";\n return { inputs: {}, extraVariables };\n }\n if (mode === \"ratio\") {\n if (!ratio) return null;\n extraVariables[platformMinReservedCashVariables[platform]] = \"\";\n return { inputs: { reserved_cash_ratio: ratio }, extraVariables };\n }\n if (mode === \"floor\") {\n if (!floor) return null;\n extraVariables[platformReservedCashRatioVariables[platform]] = \"\";\n return { inputs: { min_reserved_cash_usd: floor }, extraVariables };\n }\n if (mode === \"max\") {\n if (!floor || !ratio) return null;\n return { inputs: { min_reserved_cash_usd: floor, reserved_cash_ratio: ratio }, extraVariables };\n }\n return null;\n }\n\n function runtimeTargetOverrideForForm(form) {\n const mode = normalizeRuntimeTargetMode(form?.runtimeTargetMode);\n if (mode === \"current\") return null;\n return {\n inputs: { runtime_target_enabled_mode: mode },\n extraVariables: { [runtimeTargetEnabledVariable]: mode === \"enabled\" ? \"true\" : \"false\" },\n };\n }\n\n function incomeLayerOverrideForForm(form) {\n const defaults = incomeLayerDefaultForStrategy(form?.strategy);\n if (!defaults) return null;\n const mode = normalizeIncomeLayerMode(form?.incomeLayerMode);\n if (mode === \"current\") return null;\n const extraVariables = {};\n if (mode === \"disabled\") {\n extraVariables[incomeLayerEnabledVariable] = \"false\";\n extraVariables[incomeLayerStartUsdVariable] = \"\";\n extraVariables[incomeLayerMaxRatioVariable] = \"\";\n return { inputs: { income_layer_mode: mode }, extraVariables };\n }\n const startUsd = cleanDisplayNumber(form?.incomeLayerStartUsd || defaults.startUsd);\n const maxRatio = cleanDisplayRatio(form?.incomeLayerMaxRatio || defaults.maxRatio);\n if (!startUsd || !maxRatio) return null;\n extraVariables[incomeLayerEnabledVariable] = \"true\";\n extraVariables[incomeLayerStartUsdVariable] = startUsd;\n extraVariables[incomeLayerMaxRatioVariable] = maxRatio;\n return { inputs: { income_layer_mode: mode, income_layer_start_usd: startUsd, income_layer_max_ratio: maxRatio }, extraVariables };\n }\n\n function optionOverlayOverrideForForm(form) {\n const mode = normalizeOptionOverlayMode(form?.optionOverlayMode);\n if (mode === \"current\") return null;\n if (mode === \"enabled\" && !optionOverlaySupported(form?.strategy)) return null;\n return { inputs: { option_overlay_mode: mode } };\n }\n\n function cashOnlyExecutionOverrideForForm(form, platform = state.selected) {\n if (!platformSupportsMarginPolicy(platform)) return null;\n const mode = normalizeCashOnlyExecutionMode(form?.cashOnlyExecutionMode);\n if (mode === \"current\") return null;\n return { inputs: { cash_only_execution_mode: mode } };\n }\n\n function dcaOverrideForForm(form) {\n if (!dcaSupported(form?.strategy) || !platformSupportsDca(state.selected)) return null;\n const mode = normalizeDcaMode(form?.dcaMode);\n const baseInvestmentUsd = cleanDisplayPositiveNumber(form?.dcaBaseInvestmentUsd);\n if (!baseInvestmentUsd) return null;\n return { inputs: { dca_mode: mode, dca_base_investment_usd: baseInvestmentUsd } };\n }\n\n function mergeExtraVariables(inputs, extraVariables) {\n if (!extraVariables || !Object.keys(extraVariables).length) return;\n const merged = inputs.extra_variables_json ? JSON.parse(inputs.extra_variables_json) : {};\n Object.assign(merged, extraVariables);\n inputs.extra_variables_json = JSON.stringify(merged);\n }\n\n function buildInputs(platform = state.selected) {\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n const targetName = account.target_name || account.key;\n const inputs = {\n platform,\n target_name: targetName,\n strategy_profile: form.strategy,\n execution_mode: form.executionMode,\n variable_scope: account.variable_scope || \"default\",\n plugin_mode: normalizePluginMode(form.pluginMode),\n service_targets_mode: \"auto\",\n apply: \"true\",\n trigger_platform_sync: \"true\",\n confirm_apply: \"APPLY_AND_SYNC\",\n platform_sync_workflow: \"sync-cloud-run-env.yml\",\n };\n for (const field of [\n \"github_environment\",\n \"deployment_selector\",\n \"account_selector\",\n \"account_scope\",\n \"service_name\",\n ]) {\n if (account[field]) inputs[field] = account[field];\n }\n const reserveOverride = platformSupportsReservedCashPolicy(platform)\n ? reservePolicyOverrideForForm(form, platform)\n : null;\n if (platformSupportsReservedCashPolicy(platform)) {\n inputs.reserved_cash_policy_mode = normalizeReservePolicyMode(form.reservePolicyMode);\n if (reserveOverride) {\n Object.assign(inputs, reserveOverride.inputs);\n mergeExtraVariables(inputs, reserveOverride.extraVariables);\n }\n }\n const runtimeTargetOverride = runtimeTargetOverrideForForm(form);\n inputs.runtime_target_enabled_mode = normalizeRuntimeTargetMode(form.runtimeTargetMode);\n if (runtimeTargetOverride) {\n Object.assign(inputs, runtimeTargetOverride.inputs);\n mergeExtraVariables(inputs, runtimeTargetOverride.extraVariables);\n }\n const incomeOverride = incomeLayerOverrideForForm(form);\n inputs.income_layer_mode = normalizeIncomeLayerMode(form.incomeLayerMode);\n if (incomeOverride) {\n Object.assign(inputs, incomeOverride.inputs);\n mergeExtraVariables(inputs, incomeOverride.extraVariables);\n }\n const optionOverlayOverride = optionOverlayOverrideForForm(form);\n inputs.option_overlay_mode = normalizeOptionOverlayMode(form.optionOverlayMode);\n if (optionOverlayOverride) {\n Object.assign(inputs, optionOverlayOverride.inputs);\n }\n const cashOnlyOverride = cashOnlyExecutionOverrideForForm(form, platform);\n if (platformSupportsMarginPolicy(platform)) {\n inputs.cash_only_execution_mode = normalizeCashOnlyExecutionMode(form.cashOnlyExecutionMode);\n if (cashOnlyOverride) {\n Object.assign(inputs, cashOnlyOverride.inputs);\n }\n }\n const dcaOverride = dcaOverrideForForm(form);\n if (dcaOverride) {\n Object.assign(inputs, dcaOverride.inputs);\n }\n return inputs;\n }\n\n function reservedCashPolicyText(inputs, platform = state.selected, account = selectedAccount(platform), fallback = t(\"unchanged\")) {\n if (inputs?.reserved_cash_policy_mode === \"none\") return t(\"reservedCashNone\");\n const floor = cleanDisplayNumber(inputs?.min_reserved_cash_usd);\n const ratio = cleanDisplayRatio(inputs?.reserved_cash_ratio);\n const currency = selectedCashCurrency(platform, account);\n const hasEffectiveFloor = Boolean(floor && !(ratio && Number(floor) === 0));\n if (!hasEffectiveFloor && !ratio) return fallback;\n if (hasEffectiveFloor && ratio) return `max(${floor} ${currency}, ${formatRatioPercent(ratio)})`;\n if (hasEffectiveFloor) return `${floor} ${currency}`;\n return formatRatioPercent(ratio);\n }\n\n function platformReservedCashDefaultText(platform = state.selected, account = selectedAccount(platform)) {\n return t(\"reservedCashDefault\").replace(\"{currency}\", selectedCashCurrency(platform, account));\n }\n\n function currentReservedCashPolicyText(platform = state.selected, account = selectedAccount(platform)) {\n const entry = currentEntryForAccount(platform, account);\n const policy = currentReservePolicyForAccount(platform, account);\n return reservedCashPolicyText(\n {\n min_reserved_cash_usd: policy.minReservedCashUsd,\n reserved_cash_ratio: policy.reservedCashRatio,\n },\n platform,\n account,\n entry ? platformReservedCashDefaultText(platform, account) : t(\"notRead\"),\n );\n }\n\n function pendingReservedCashPolicyText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n return reservedCashPolicyText(pendingReservePolicy(inputs, platform, account).inputs, platform, account, t(\"unchanged\"));\n }\n\n function pendingReservePolicy(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const current = currentReservePolicyForAccount(platform, account);\n const currentFloor = cleanDisplayNumber(current.minReservedCashUsd);\n const currentRatio = cleanDisplayRatio(current.reservedCashRatio);\n const mode = normalizeReservePolicyMode(inputs.reserved_cash_policy_mode);\n const next = {\n min_reserved_cash_usd: cleanDisplayNumber(inputs.min_reserved_cash_usd),\n reserved_cash_ratio: cleanDisplayRatio(inputs.reserved_cash_ratio),\n };\n if (mode === \"none\") {\n next.reserved_cash_policy_mode = \"none\";\n }\n const entry = currentEntryForAccount(platform, account);\n const changed = Boolean(entry && (\n next.min_reserved_cash_usd !== currentFloor ||\n next.reserved_cash_ratio !== currentRatio ||\n (mode === \"none\" && (currentFloor || currentRatio))\n ));\n return { changed, inputs: next };\n }\n\n function currentIncomeLayerText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return t(\"incomeLayerNotSupported\");\n const entry = currentEntryForAccount(platform, account);\n if (!entry) return t(\"notRead\");\n const current = incomeLayerFromEntry(entry);\n if (!incomeLayerFieldsConfigured(entry)) {\n return t(\"incomeLayerDefault\")\n .replace(\"{start}\", formatUsd(defaults.startUsd))\n .replace(\"{ratio}\", formatRatioPercent(defaults.maxRatio));\n }\n const enabled = current.enabled ?? true;\n const startUsd = current.startUsd || String(defaults.startUsd);\n const ratio = current.maxRatio || defaults.maxRatio;\n return enabled\n ? t(\"incomeLayerOn\")\n .replace(\"{start}\", formatUsd(startUsd))\n .replace(\"{ratio}\", formatRatioPercent(ratio))\n : t(\"incomeLayerOff\");\n }\n\n function pendingIncomeLayerText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingIncomeLayer(inputs, platform, account);\n if (!pending.supported) return t(\"incomeLayerNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n if (pending.inputs.income_layer_enabled === false) return t(\"incomeLayerOff\");\n return t(\"incomeLayerOn\")\n .replace(\"{start}\", formatUsd(pending.inputs.income_layer_start_usd))\n .replace(\"{ratio}\", formatRatioPercent(pending.inputs.income_layer_max_ratio));\n }\n\n function pendingOptionOverlayText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingOptionOverlay(inputs, platform, account);\n if (!pending.supported && pending.inputs.option_overlay_enabled !== false) return t(\"optionOverlayNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n return optionOverlayText(pending.inputs.option_overlay_enabled);\n }\n\n function pendingCashOnlyExecutionText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingCashOnlyExecution(inputs, platform, account);\n if (!pending.changed) return t(\"unchanged\");\n return cashOnlyExecutionText(pending.inputs.cash_only_execution);\n }\n\n function pendingRuntimeTargetText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingRuntimeTarget(inputs, platform, account);\n if (!pending.changed) return t(\"unchanged\");\n return runtimeTargetText(pending.inputs.runtime_target_enabled);\n }\n\n function pendingRuntimeTargetTone(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingRuntimeTarget(inputs, platform, account);\n if (!pending.changed) return \"neutral\";\n return runtimeTargetTone(pending.inputs.runtime_target_enabled);\n }\n\n function currentDcaText(platform = state.selected, account = selectedAccount(platform), profile = state.forms[platform]?.strategy) {\n const current = currentDcaForAccount(platform, account, profile);\n if (!current.supported) return t(\"dcaNotSupported\");\n return t(\"dcaText\")\n .replace(\"{mode}\", dcaModeLabel(current.mode))\n .replace(\"{amount}\", formatUsd(current.baseInvestmentUsd));\n }\n\n function pendingDcaText(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const pending = pendingDca(inputs, platform, account);\n if (!pending.supported) return t(\"dcaNotSupported\");\n if (!pending.changed) return t(\"unchanged\");\n return t(\"dcaText\")\n .replace(\"{mode}\", dcaModeLabel(pending.inputs.dca_mode))\n .replace(\"{amount}\", formatUsd(pending.inputs.dca_base_investment_usd));\n }\n\n function pendingRuntimeTarget(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const mode = normalizeRuntimeTargetMode(inputs.runtime_target_enabled_mode);\n if (mode === \"current\") {\n return {\n changed: false,\n inputs: { runtime_target_enabled: runtimeTargetEnabledForAccount(platform, account) ?? true },\n };\n }\n const current = runtimeTargetEnabledForAccount(platform, account);\n const currentEnabled = current ?? true;\n const nextEnabled = mode === \"enabled\";\n const entry = currentEntryForAccount(platform, account);\n return {\n changed: Boolean(entry && current !== null && currentEnabled !== nextEnabled),\n inputs: { runtime_target_enabled: nextEnabled },\n };\n }\n\n function pendingIncomeLayer(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const defaults = incomeLayerDefaultForStrategy(profile);\n if (!defaults) return { supported: false, changed: false, inputs: {} };\n const mode = normalizeIncomeLayerMode(inputs.income_layer_mode);\n const entry = currentEntryForAccount(platform, account);\n const rawCurrent = currentIncomeLayerForAccount(platform, account);\n const effective = effectiveIncomeLayerForAccount(platform, account, profile);\n const currentEnabled = effective?.enabled ?? true;\n const currentStartUsd = effective?.startUsd ?? String(defaults.startUsd);\n const currentRatio = effective?.maxRatio ?? defaults.maxRatio;\n if (mode === \"current\") {\n return {\n supported: true,\n changed: false,\n inputs: {\n income_layer_enabled: rawCurrent.enabled,\n income_layer_start_usd: rawCurrent.startUsd,\n income_layer_max_ratio: rawCurrent.maxRatio,\n },\n };\n }\n if (mode === \"disabled\") {\n if (!entry) {\n return {\n supported: true,\n changed: false,\n inputs: {\n income_layer_enabled: false,\n income_layer_start_usd: \"\",\n income_layer_max_ratio: \"\",\n },\n };\n }\n return {\n supported: true,\n changed: currentEnabled !== false || Boolean(rawCurrent.startUsd || rawCurrent.maxRatio),\n inputs: {\n income_layer_enabled: false,\n income_layer_start_usd: \"\",\n income_layer_max_ratio: \"\",\n },\n };\n }\n const nextStartUsd = cleanDisplayNumber(inputs.income_layer_start_usd || defaults.startUsd);\n const nextRatio = cleanDisplayRatio(inputs.income_layer_max_ratio || defaults.maxRatio);\n return {\n supported: true,\n changed: Boolean(entry && (currentEnabled !== true || nextStartUsd !== currentStartUsd || nextRatio !== currentRatio)),\n inputs: {\n income_layer_enabled: true,\n income_layer_start_usd: nextStartUsd,\n income_layer_max_ratio: nextRatio,\n },\n };\n }\n\n function pendingOptionOverlay(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const supported = optionOverlaySupported(profile);\n const mode = normalizeOptionOverlayMode(inputs.option_overlay_mode);\n const current = effectiveOptionOverlayForAccount(platform, account, profile);\n if (mode === \"current\") {\n return {\n supported,\n changed: false,\n inputs: { option_overlay_enabled: currentOptionOverlayForAccount(platform, account) },\n };\n }\n if (mode === \"enabled\") {\n return {\n supported,\n changed: supported && current !== null && current !== true,\n inputs: { option_overlay_enabled: true },\n };\n }\n return {\n supported,\n changed: current === true,\n inputs: { option_overlay_enabled: false },\n };\n }\n\n function pendingCashOnlyExecution(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const mode = normalizeCashOnlyExecutionMode(inputs.cash_only_execution_mode);\n if (!platformSupportsMarginPolicy(platform) || mode === \"current\") {\n return { changed: false, inputs: {} };\n }\n const current = effectiveCashOnlyExecutionForAccount(platform, account);\n const nextEnabled = mode === \"enabled\";\n const entry = currentEntryForAccount(platform, account);\n return {\n changed: Boolean(entry && current !== null && current !== nextEnabled),\n inputs: { cash_only_execution: nextEnabled },\n };\n }\n\n function pendingDca(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const profile = cleanStrategyProfile(inputs.strategy_profile || state.forms[platform]?.strategy);\n const defaults = dcaConfigForStrategy(profile);\n if (!defaults) return { supported: false, changed: false, inputs: {} };\n const current = currentDcaForAccount(platform, account, profile);\n const nextMode = normalizeDcaMode(inputs.dca_mode || defaults.defaultMode);\n const nextBase = cleanDisplayPositiveNumber(inputs.dca_base_investment_usd || defaults.defaultBaseInvestmentUsd);\n return {\n supported: true,\n changed: Boolean(current.mode !== nextMode || current.baseInvestmentUsd !== nextBase),\n inputs: {\n dca_mode: nextMode,\n dca_base_investment_usd: nextBase,\n },\n };\n }\n\n function pendingChangeState(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const currentProfile = currentStrategyForAccount(platform, account);\n const nextProfile = cleanStrategyProfile(inputs.strategy_profile);\n const currentEntry = currentEntryForAccount(platform, account);\n const currentMode = normalizeExecutionMode(currentEntry?.execution_mode, currentEntry?.dry_run_only);\n const currentPluginMode = currentPluginModeForAccount(platform, account);\n const nextPluginMode = normalizePluginMode(inputs.plugin_mode);\n const runtimeTarget = pendingRuntimeTarget(inputs, platform, account);\n const reserve = pendingReservePolicy(inputs, platform, account);\n const income = pendingIncomeLayer(inputs, platform, account);\n const optionOverlay = pendingOptionOverlay(inputs, platform, account);\n const cashOnly = pendingCashOnlyExecution(inputs, platform, account);\n const dca = pendingDca(inputs, platform, account);\n return {\n currentProfile,\n nextProfile,\n currentMode,\n currentPluginMode,\n nextPluginMode,\n strategyChanged: Boolean(nextProfile && ((state.forms[platform]?.strategyTouched) || (currentProfile && currentProfile !== nextProfile))),\n modeChanged: Boolean(inputs.execution_mode && currentMode && currentMode !== inputs.execution_mode),\n pluginModeChanged: Boolean(nextPluginMode && currentPluginMode && currentPluginMode !== nextPluginMode),\n runtimeTargetChanged: runtimeTarget.changed,\n reserveCashChanged: reserve.changed,\n incomeLayerChanged: income.changed,\n optionOverlayChanged: optionOverlay.changed,\n cashOnlyChanged: cashOnly.changed,\n dcaChanged: dca.changed,\n runtimeTarget,\n reserve,\n income,\n optionOverlay,\n cashOnly,\n dca,\n };\n }\n\n function hasPendingChanges(inputs, platform = state.selected, account = selectedAccount(platform)) {\n const changes = pendingChangeState(inputs, platform, account);\n return Boolean(\n changes.strategyChanged ||\n changes.modeChanged ||\n changes.pluginModeChanged ||\n changes.runtimeTargetChanged ||\n changes.reserveCashChanged ||\n changes.incomeLayerChanged ||\n changes.optionOverlayChanged ||\n changes.cashOnlyChanged ||\n changes.dcaChanged\n );\n }\n\n function formatRatioPercent(value) {\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return String(value);\n return `${(numeric * 100).toFixed(2).replace(/\\.?0+$/, \"\")}%`;\n }\n\n function formatUsd(value) {\n const numeric = Number(value);\n if (!Number.isFinite(numeric)) return String(value);\n return `$${numeric.toLocaleString(\"en-US\", { maximumFractionDigits: 0 })}`;\n }\n\n function incomeLayerAllocationText(defaults) {\n if (!defaults?.allocations) return \"\";\n return Object.entries(defaults.allocations)\n .map(([symbol, ratio]) => `${symbol} ${formatRatioPercent(ratio)}`)\n .join(\" / \");\n }\n\n function incomeLayerDefaultMetaText(defaults) {\n if (!defaults) return t(\"incomeLayerModeMeta\");\n return t(\"incomeLayerDefaultMeta\")\n .replace(\"{start}\", formatUsd(defaults.startUsd))\n .replace(\"{ratio}\", formatRatioPercent(defaults.maxRatio));\n }\n\n function optionOverlayDefaultMetaText(defaults) {\n if (!defaults?.families?.length) return t(\"optionOverlayModeMeta\");\n const familyText = defaults.families.map((item) => {\n const family = item.family === \"income\" ? t(\"optionOverlayFamilyIncome\") : t(\"optionOverlayFamilyGrowth\");\n const ratioText = item.ratioKind === \"risk\"\n ? t(\"optionOverlayRiskRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio))\n : t(\"optionOverlayBudgetRatio\").replace(\"{ratio}\", formatRatioPercent(item.ratio));\n return `${family}: ${item.recipe}, ${formatUsd(item.startUsd)}, ${ratioText}`;\n }).join(\" / \");\n return t(\"optionOverlayDefaultMeta\").replace(\"{defaults}\", familyText);\n }\n\n function summaryRows(inputs) {\n const account = selectedAccount();\n const changes = pendingChangeState(inputs, state.selected, account);\n const currentStrategyText = changes.currentProfile ? strategyLabel(changes.currentProfile) : t(\"notRead\");\n const rows = [\n [t(\"repository\"), state.repositories[state.selected] || defaultRepositories[state.selected]],\n [t(\"selectedAccount\"), account.label],\n [t(\"currentStrategy\"), currentStrategyText],\n [t(\"selectedMarket\"), supportedDomainLabel(state.selected, account)],\n [\n t(\"currentRuntimeTarget\"),\n currentRuntimeTargetText(state.selected, account),\n \"\",\n currentRuntimeTargetTone(state.selected, account),\n ],\n [t(\"currentPluginMode\"), pluginModeLabel(changes.currentPluginMode)],\n [t(\"reservedCashPolicy\"), currentReservedCashPolicyText(state.selected, account)],\n ];\n if (platformSupportsMarginPolicy(state.selected)) {\n rows.push([t(\"currentCashOnlyExecution\"), currentCashOnlyExecutionText(state.selected, account)]);\n }\n if (incomeLayerSupported(inputs.strategy_profile)) {\n rows.push([t(\"currentIncomeLayer\"), currentIncomeLayerText(state.selected, account, inputs.strategy_profile)]);\n }\n if (optionOverlaySupported(inputs.strategy_profile) || changes.optionOverlayChanged) {\n rows.push([t(\"currentOptionOverlay\"), currentOptionOverlayText(state.selected, account, inputs.strategy_profile)]);\n }\n if (dcaSupported(inputs.strategy_profile)) {\n rows.push([t(\"currentDca\"), currentDcaText(state.selected, account, inputs.strategy_profile)]);\n }\n if (changes.reserveCashChanged) {\n rows.push([t(\"pendingReservedCashPolicy\"), pendingReservedCashPolicyText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.incomeLayerChanged) {\n rows.push([t(\"pendingIncomeLayer\"), pendingIncomeLayerText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.optionOverlayChanged) {\n rows.push([t(\"pendingOptionOverlay\"), pendingOptionOverlayText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.cashOnlyChanged) {\n rows.push([t(\"pendingCashOnlyExecution\"), pendingCashOnlyExecutionText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.dcaChanged) {\n rows.push([t(\"pendingDca\"), pendingDcaText(inputs, state.selected, account), \"pending\"]);\n }\n if (changes.modeChanged) {\n rows.push([t(\"pendingMode\"), modeLabel(inputs.execution_mode), \"pending\"]);\n }\n if (changes.pluginModeChanged) {\n rows.push([t(\"pendingPluginMode\"), pluginModeLabel(changes.nextPluginMode), \"pending\"]);\n }\n if (changes.runtimeTargetChanged) {\n rows.push([\n t(\"pendingRuntimeTarget\"),\n pendingRuntimeTargetText(inputs, state.selected, account),\n \"pending\",\n pendingRuntimeTargetTone(inputs, state.selected, account),\n ]);\n }\n if (changes.strategyChanged && changes.nextProfile) {\n rows.push([t(\"nextStrategy\"), strategyLabel(changes.nextProfile), \"pending\"]);\n }\n return rows;\n }\n\n function applyLanguage() {\n document.documentElement.lang = state.lang === \"zh\" ? \"zh-CN\" : \"en\";\n document.querySelectorAll(\"[data-i18n]\").forEach((node) => {\n node.textContent = t(node.dataset.i18n);\n });\n el(\"lang-button\").textContent = state.lang === \"zh\" ? \"EN\" : \"中\";\n }\n\n function renderRuntimeAuthorityStatus() {\n const status = String(runtimeAuthorityStatus.status || \"UNKNOWN_RUNTIME_AUTHORITY_STATUS\");\n const asOf = String(runtimeAuthorityStatus.status_as_of || \"—\");\n el(\"runtime-authority-status\").textContent = status;\n el(\"runtime-authority-as-of\").textContent = t(\"runtimeAuthorityAsOf\").replace(\"{asOf}\", asOf);\n }\n\n function renderPlatforms() {\n const strip = el(\"platform-strip\");\n strip.replaceChildren();\n const showPrivateConfig = hasPrivateConfig();\n for (const platform of Object.keys(platformMeta)) {\n ensureAccountSelection(platform);\n const meta = platformMeta[platform];\n const form = state.forms[platform];\n const account = selectedAccount(platform);\n const button = document.createElement(\"button\");\n button.className = \"platform-button\";\n button.type = \"button\";\n button.dataset.platform = platform;\n button.classList.toggle(\"active\", platform === state.selected);\n const mark = document.createElement(\"span\");\n mark.className = \"mark\";\n mark.textContent = meta.code;\n const copyNode = document.createElement(\"span\");\n copyNode.className = \"platform-copy\";\n const labelNode = document.createElement(\"strong\");\n labelNode.textContent = meta.label;\n copyNode.append(labelNode);\n if (showPrivateConfig) {\n const accountNode = document.createElement(\"span\");\n accountNode.textContent = account.label;\n const strategyNode = document.createElement(\"small\");\n strategyNode.textContent = strategyLabel(form.strategy);\n copyNode.append(accountNode, strategyNode);\n }\n button.append(mark, copyNode);\n strip.appendChild(button);\n }\n }\n\n function renderControls() {\n const platform = state.selected;\n const meta = platformMeta[platform];\n const form = state.forms[platform];\n const accounts = optionsFor(platform);\n const account = selectedAccount(platform);\n const choices = strategyChoicesForAccount(platform, account, form.executionMode);\n const currentStrategy = currentStrategyForAccount(platform, account);\n const currentStrategyBlocked = Boolean(\n currentStrategy &&\n !strategyAllowedForAccount(platform, account, currentStrategy, form.executionMode),\n );\n const accountSelect = el(\"account-select\");\n const strategySelect = el(\"strategy-select\");\n const runtimeTargetEnabledSelect = el(\"runtime-target-enabled-select\");\n const pluginModeSelect = el(\"plugin-mode-select\");\n const incomeLayerModeSelect = el(\"income-layer-mode-select\");\n const incomeLayerStartUsdInput = el(\"income-layer-start-usd-input\");\n const incomeLayerMaxRatioInput = el(\"income-layer-max-ratio-input\");\n const optionOverlayModeSelect = el(\"option-overlay-mode-select\");\n const cashOnlyExecutionModeSelect = el(\"cash-only-execution-mode-select\");\n const dcaModeSelect = el(\"dca-mode-select\");\n const dcaBaseInvestmentUsdInput = el(\"dca-base-investment-usd-input\");\n const reservePolicyModeSelect = el(\"reserve-policy-mode-select\");\n const minReservedCashInput = el(\"min-reserved-cash-input\");\n const reservedCashRatioInput = el(\"reserved-cash-ratio-input\");\n const showPrivateControls = hasPrivateConfig();\n\n el(\"switch-panel\").style.setProperty(\"--platform-color\", meta.accent);\n el(\"platform-title\").textContent = meta.label;\n el(\"quick-form\").hidden = !showPrivateControls;\n el(\"run-area\").hidden = !showPrivateControls;\n el(\"public-note\").hidden = showPrivateControls;\n el(\"public-preview\").hidden = showPrivateControls;\n el(\"public-note\").textContent = state.auth.allowed ? t(\"missingConfigNote\") : t(\"publicReadonly\");\n\n if (!showPrivateControls) {\n accountSelect.replaceChildren();\n strategySelect.replaceChildren();\n runtimeTargetEnabledSelect.replaceChildren();\n pluginModeSelect.replaceChildren();\n incomeLayerModeSelect.replaceChildren();\n optionOverlayModeSelect.replaceChildren();\n cashOnlyExecutionModeSelect.replaceChildren();\n dcaModeSelect.replaceChildren();\n reservePolicyModeSelect.replaceChildren();\n incomeLayerStartUsdInput.value = \"\";\n incomeLayerMaxRatioInput.value = \"\";\n dcaBaseInvestmentUsdInput.value = \"\";\n minReservedCashInput.value = \"\";\n reservedCashRatioInput.value = \"\";\n el(\"account-meta\").textContent = \"\";\n el(\"strategy-meta\").textContent = \"\";\n el(\"income-layer-mode-meta\").textContent = \"\";\n el(\"income-layer-start-meta\").textContent = \"\";\n el(\"income-layer-ratio-meta\").textContent = \"\";\n el(\"option-overlay-mode-meta\").textContent = \"\";\n el(\"cash-only-execution-mode-meta\").textContent = \"\";\n el(\"dca-mode-meta\").textContent = \"\";\n el(\"dca-base-meta\").textContent = \"\";\n return;\n }\n\n accountSelect.replaceChildren();\n if (accounts.length) {\n for (const account of accounts) {\n accountSelect.append(new Option(account.label, account.key, false, account.key === form.accountKey));\n }\n } else {\n accountSelect.append(new Option(t(\"noAccount\"), \"\"));\n }\n el(\"account-meta\").textContent = accounts.length ? accountMetaText(platform) : \"\";\n\n if (choices.length && !choices.includes(form.strategy) && !currentStrategyBlocked) {\n form.strategy = choices[0];\n }\n strategySelect.disabled = !choices.length;\n strategySelect.replaceChildren();\n if (currentStrategyBlocked) {\n const blockedOption = new Option(\n strategyChoiceLabel(currentStrategy, platform, account, form.executionMode),\n currentStrategy,\n true,\n currentStrategy === form.strategy,\n );\n blockedOption.disabled = true;\n strategySelect.append(blockedOption);\n }\n if (choices.length) {\n for (const strategy of choices) {\n strategySelect.append(\n new Option(strategyChoiceLabel(strategy, platform, account, form.executionMode), strategy, false, strategy === form.strategy),\n );\n }\n } else {\n strategySelect.append(new Option(t(\"noStrategy\"), \"\"));\n }\n el(\"strategy-meta\").textContent = account\n ? strategyDisplayMetaText(platform, account, form.strategy)\n : \"\";\n runtimeTargetEnabledSelect.replaceChildren();\n for (const mode of runtimeTargetModes) {\n runtimeTargetEnabledSelect.append(\n new Option(runtimeTargetModeLabel(mode), mode, false, mode === normalizeRuntimeTargetMode(form.runtimeTargetMode)),\n );\n }\n pluginModeSelect.replaceChildren();\n for (const mode of pluginModes) {\n pluginModeSelect.append(new Option(pluginModeLabel(mode), mode, false, mode === normalizePluginMode(form.pluginMode)));\n }\n const incomeDefaults = incomeLayerDefaultForStrategy(form.strategy);\n el(\"income-layer-section\").hidden = false;\n el(\"option-overlay-section\").hidden = false;\n incomeLayerModeSelect.replaceChildren();\n if (incomeDefaults) {\n incomeLayerModeSelect.disabled = false;\n for (const mode of incomeLayerModes) {\n incomeLayerModeSelect.append(new Option(incomeLayerModeLabel(mode), mode, false, mode === normalizeIncomeLayerMode(form.incomeLayerMode)));\n }\n el(\"income-layer-mode-meta\").textContent = incomeLayerDefaultMetaText(incomeDefaults);\n el(\"income-layer-start-meta\").textContent = t(\"incomeLayerStartMeta\");\n el(\"income-layer-ratio-meta\").textContent = t(\"incomeLayerAllocationMeta\").replace(\n \"{allocations}\",\n incomeLayerAllocationText(incomeDefaults),\n );\n } else {\n incomeLayerModeSelect.disabled = true;\n incomeLayerModeSelect.append(new Option(t(\"incomeLayerNotSupported\"), \"current\"));\n el(\"income-layer-mode-meta\").textContent = t(\"incomeLayerModeMeta\");\n el(\"income-layer-start-meta\").textContent = t(\"incomeLayerStartMeta\");\n el(\"income-layer-ratio-meta\").textContent = t(\"incomeLayerRatioMeta\");\n }\n const supportsMargin = platformSupportsMarginPolicy(platform);\n const supportsReserve = platformSupportsReservedCashPolicy(platform);\n if (supportsMargin) syncCashOnlyExecutionForAccount(platform);\n reconcileExecutionCashPolicy(form, \"margin\");\n const executionCashPolicyGrid = el(\"execution-cash-policy-grid\");\n const qmtPlatformCashNote = el(\"qmt-platform-cash-note\");\n const executionCashPolicyNote = el(\"execution-cash-policy-note\");\n executionCashPolicyGrid.hidden = !supportsMargin && !supportsReserve;\n qmtPlatformCashNote.hidden = supportsMargin || supportsReserve || platform !== \"qmt\";\n executionCashPolicyNote.hidden = !supportsMargin || !supportsReserve;\n\n const marginBlocksReserve = supportsMargin && supportsReserve && allowMarginExplicitlySelected(form);\n const reserveBlocksMargin = supportsMargin && supportsReserve && reserveCashOverrideActive(form);\n\n if (supportsReserve) {\n reservePolicyModeSelect.replaceChildren();\n for (const mode of reservePolicyModes) {\n reservePolicyModeSelect.append(new Option(t(`reservePolicy${mode[0].toUpperCase()}${mode.slice(1)}`), mode, false, mode === normalizeReservePolicyMode(form.reservePolicyMode)));\n }\n const reserveMode = normalizeReservePolicyMode(form.reservePolicyMode);\n el(\"min-reserved-cash-label\").textContent = t(\"minReservedCash\").replace(\n \"{currency}\",\n selectedCashCurrency(platform, account),\n );\n reservePolicyModeSelect.disabled = false;\n minReservedCashInput.disabled = reserveMode === \"current\" || reserveMode === \"none\" || reserveMode === \"ratio\";\n reservedCashRatioInput.disabled = reserveMode === \"current\" || reserveMode === \"none\" || reserveMode === \"floor\";\n minReservedCashInput.value = reserveMode === \"ratio\" || reserveMode === \"none\" ? \"\" : form.minReservedCashUsd;\n reservedCashRatioInput.value = reserveMode === \"floor\" || reserveMode === \"none\" ? \"\" : form.reservedCashRatio;\n el(\"reserve-policy-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"min-reserve-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"reserve-ratio-block\").classList.toggle(\"policy-block-muted\", marginBlocksReserve);\n el(\"reserve-policy-mode-meta\").textContent = marginBlocksReserve\n ? t(\"executionCashMarginBlocksReserve\")\n : t(\"reservedCashModeMeta\");\n } else {\n reservePolicyModeSelect.replaceChildren();\n minReservedCashInput.value = \"\";\n reservedCashRatioInput.value = \"\";\n }\n const incomeMode = normalizeIncomeLayerMode(form.incomeLayerMode);\n const incomeLayerInputsDisabled = !incomeDefaults || incomeMode === \"disabled\";\n incomeLayerStartUsdInput.disabled = incomeLayerInputsDisabled;\n incomeLayerMaxRatioInput.disabled = incomeLayerInputsDisabled;\n if (incomeDefaults && incomeMode !== \"disabled\" && !cleanDisplayNumber(form.incomeLayerStartUsd)) {\n form.incomeLayerStartUsd = String(incomeDefaults.startUsd);\n }\n if (incomeDefaults && incomeMode !== \"disabled\" && !cleanDisplayRatio(form.incomeLayerMaxRatio)) {\n form.incomeLayerMaxRatio = incomeDefaults.maxRatio;\n }\n incomeLayerStartUsdInput.value = incomeDefaults && incomeMode !== \"disabled\" ? form.incomeLayerStartUsd : \"\";\n incomeLayerMaxRatioInput.value = incomeDefaults && incomeMode !== \"disabled\" ? form.incomeLayerMaxRatio : \"\";\n\n const optionDefaults = optionOverlayDefaultForStrategy(form.strategy);\n optionOverlayModeSelect.replaceChildren();\n if (optionDefaults) {\n optionOverlayModeSelect.disabled = false;\n for (const mode of optionOverlayModes) {\n optionOverlayModeSelect.append(\n new Option(optionOverlayModeLabel(mode), mode, false, mode === normalizeOptionOverlayMode(form.optionOverlayMode)),\n );\n }\n el(\"option-overlay-mode-meta\").textContent = optionOverlayDefaultMetaText(optionDefaults);\n } else {\n optionOverlayModeSelect.disabled = true;\n optionOverlayModeSelect.append(new Option(t(\"optionOverlayNotSupported\"), \"current\"));\n el(\"option-overlay-mode-meta\").textContent = t(\"optionOverlayModeMeta\");\n }\n\n if (supportsMargin) {\n cashOnlyExecutionModeSelect.replaceChildren();\n for (const mode of cashOnlyExecutionModes) {\n const option = new Option(\n mode === \"enabled\" ? t(\"cashOnlyExecutionNo\") : t(\"cashOnlyExecutionYes\"),\n mode,\n false,\n mode === normalizeCashOnlyExecutionMode(form.cashOnlyExecutionMode),\n );\n cashOnlyExecutionModeSelect.append(option);\n }\n el(\"cash-only-policy-block\").classList.toggle(\"policy-block-muted\", reserveBlocksMargin);\n el(\"cash-only-execution-mode-meta\").textContent = reserveBlocksMargin\n ? t(\"executionCashReserveBlocksMargin\")\n : t(\"cashOnlyExecutionModeMeta\");\n } else {\n cashOnlyExecutionModeSelect.replaceChildren();\n el(\"cash-only-execution-mode-meta\").textContent = \"\";\n }\n\n const dcaDefaults = dcaConfigForStrategy(form.strategy);\n dcaModeSelect.replaceChildren();\n const dcaAllowed = Boolean(dcaDefaults) && platformSupportsDca(platform);\n if (dcaAllowed) {\n dcaModeSelect.disabled = false;\n for (const mode of dcaModes) {\n dcaModeSelect.append(new Option(dcaModeLabel(mode), mode, false, mode === normalizeDcaMode(form.dcaMode)));\n }\n if (!cleanDisplayPositiveNumber(form.dcaBaseInvestmentUsd)) {\n form.dcaBaseInvestmentUsd = dcaDefaults.defaultBaseInvestmentUsd;\n }\n dcaBaseInvestmentUsdInput.disabled = false;\n dcaBaseInvestmentUsdInput.value = form.dcaBaseInvestmentUsd;\n el(\"dca-mode-meta\").textContent = t(\"dcaDefaultMeta\")\n .replace(\"{mode}\", dcaModeLabel(dcaDefaults.defaultMode))\n .replace(\"{amount}\", formatUsd(dcaDefaults.defaultBaseInvestmentUsd));\n el(\"dca-base-meta\").textContent = t(\"dcaModeMeta\");\n } else {\n dcaModeSelect.disabled = true;\n dcaModeSelect.append(new Option(\n dcaDefaults && !platformSupportsDca(platform) ? t(\"dcaPlatformNotSupported\") : t(\"dcaNotSupported\"),\n \"fixed\",\n ));\n dcaBaseInvestmentUsdInput.disabled = true;\n dcaBaseInvestmentUsdInput.value = \"\";\n el(\"dca-mode-meta\").textContent = t(\"dcaModeMeta\");\n el(\"dca-base-meta\").textContent = t(\"dcaModeMeta\");\n }\n\n const supportedModes = supportedExecutionModesForPlatform(platform);\n const liveModeAvailable = supportedModes.includes(\"live\") && hasLiveStrategyOption(platform, account);\n if (!supportedModes.includes(form.executionMode)) form.executionMode = \"dry_run\";\n document.querySelectorAll(\"#mode-control [data-mode]\").forEach((button) => {\n button.disabled = !supportedModes.includes(button.dataset.mode) || (\n button.dataset.mode === \"live\" && !liveModeAvailable\n );\n button.classList.toggle(\"active\", button.dataset.mode === form.executionMode);\n });\n el(\"mode-meta\").textContent = !supportedModes.includes(\"live\")\n ? t(\"qmtDryRunOnlyNote\")\n : (!liveModeAvailable ? t(\"liveModeUnavailable\") : \"\");\n }\n\n function renderSummary() {\n const showSummary = hasPrivateConfig();\n const summaryPanel = document.querySelector(\".summary-panel\");\n const switchSurface = document.querySelector(\".switch-surface\");\n summaryPanel.hidden = !showSummary;\n switchSurface.classList.toggle(\"summary-hidden\", !showSummary);\n if (!showSummary) return;\n\n const inputs = buildInputs();\n const list = el(\"summary-list\");\n list.replaceChildren();\n document.querySelector(\".summary-head h2\").textContent = t(\"summary\");\n const currentHeading = document.createElement(\"p\");\n currentHeading.className = \"summary-section-title\";\n currentHeading.textContent = t(\"summaryCurrent\");\n list.appendChild(currentHeading);\n let pendingSectionInserted = false;\n for (const [label, value, rowClass, valueTone] of summaryRows(inputs)) {\n if (!pendingSectionInserted && rowClass === \"pending\") {\n const pendingHeading = document.createElement(\"p\");\n pendingHeading.className = \"summary-section-title\";\n pendingHeading.textContent = t(\"summaryPending\");\n list.appendChild(pendingHeading);\n pendingSectionInserted = true;\n }\n const row = document.createElement(\"div\");\n row.className = \"summary-row\";\n row.setAttribute(\"role\", \"listitem\");\n if (rowClass) row.classList.add(rowClass);\n const labelNode = document.createElement(\"div\");\n labelNode.className = \"summary-label\";\n labelNode.textContent = label;\n const valueNode = document.createElement(\"div\");\n valueNode.className = \"summary-value\";\n if (valueTone) {\n const badge = document.createElement(\"span\");\n badge.className = `summary-status ${valueTone}`;\n badge.textContent = value;\n valueNode.appendChild(badge);\n } else {\n valueNode.textContent = value;\n }\n row.append(labelNode, valueNode);\n list.appendChild(row);\n }\n\n const account = selectedAccount();\n const currentEntry = currentEntryForAccount(state.selected, account);\n const currentMode = normalizeExecutionMode(currentEntry?.execution_mode, currentEntry?.dry_run_only);\n el(\"mode-pill\").textContent = currentMode ? modeLabel(currentMode) : t(\"notRead\");\n }\n\n function setPlanCheck(id, value, tone) {\n const node = el(id);\n if (!node) return;\n node.textContent = value;\n node.dataset.tone = tone || \"neutral\";\n }\n\n function renderPlanReadiness() {\n const platform = state.selected;\n const account = selectedAccount(platform);\n const configured = hasPrivateConfig() && Boolean(account?.key);\n const runnable = configured && hasRunnableStrategySelection(platform);\n const riskValid = configured && hasValidExecutionCashPolicy(platform) &&\n hasValidIncomeLayerPolicy(platform) && hasValidOptionOverlayPolicy(platform) && hasValidDcaPolicy(platform);\n const form = state.forms[platform];\n const isNonLive = normalizeExecutionMode(form?.executionMode, false) !== \"live\";\n setPlanCheck(\n \"plan-check-account\",\n configured ? t(\"planCheckSelected\") : t(\"planCheckWaiting\"),\n configured ? \"ready\" : \"neutral\",\n );\n setPlanCheck(\n \"plan-check-strategy\",\n runnable ? t(\"planCheckValid\") : (configured ? t(\"planCheckFix\") : t(\"planCheckWaiting\")),\n runnable ? \"ready\" : (configured ? \"warning\" : \"neutral\"),\n );\n setPlanCheck(\n \"plan-check-risk\",\n riskValid ? t(\"planCheckValid\") : (configured ? t(\"planCheckFix\") : t(\"planCheckWaiting\")),\n riskValid ? \"ready\" : (configured ? \"warning\" : \"neutral\"),\n );\n setPlanCheck(\n \"plan-check-authority\",\n isNonLive ? t(\"planCheckNonLive\") : t(\"planCheckNoAuthority\"),\n isNonLive ? \"ready\" : \"warning\",\n );\n }\n\n function renderAuth() {\n const status = el(\"auth-status\");\n const loginLink = el(\"login-link\");\n const logoutButton = el(\"logout-button\");\n const signedIn = Boolean(state.auth.allowed && state.auth.login);\n\n status.hidden = !signedIn;\n status.textContent = signedIn ? t(\"signedInAs\").replace(\"{login}\", state.auth.login) : \"\";\n loginLink.hidden = signedIn;\n loginLink.href = \"/login\";\n loginLink.textContent = t(\"login\");\n logoutButton.hidden = !signedIn;\n logoutButton.textContent = t(\"logout\");\n\n const dispatch = el(\"dispatch-button\");\n const hasPrivateAccounts = state.configSource === \"private\";\n const loadingConfig = state.configSource === \"loading\";\n const hasRunnableStrategy = hasRunnableStrategySelection();\n const hasValidReserve = hasValidExecutionCashPolicy();\n const hasValidIncomeLayer = hasValidIncomeLayerPolicy();\n const hasValidOptionOverlay = hasValidOptionOverlayPolicy();\n const hasValidDca = hasValidDcaPolicy();\n const hasValidStrategy = hasRunnableStrategy &&\n hasValidReserve &&\n hasValidIncomeLayer &&\n hasValidOptionOverlay &&\n hasValidDca;\n const hasPendingChange = hasPrivateAccounts && hasValidStrategy && hasPendingChanges(buildInputs());\n dispatch.disabled = !state.auth.allowed || loadingConfig || !hasPrivateAccounts || !hasValidStrategy || !hasPendingChange;\n dispatch.textContent = state.auth.allowed\n ? (loadingConfig\n ? t(\"loadingConfig\")\n : (hasPrivateAccounts ? (hasValidStrategy ? (hasPendingChange ? t(\"runSwitch\") : t(\"noChanges\")) : t(\"configureAccounts\")) : t(\"configureAccounts\")))\n : t(\"loginToRun\");\n const note = el(\"action-note\");\n note.textContent = state.auth.allowed\n ? (loadingConfig\n ? t(\"loadingConfigNote\")\n : (hasPrivateAccounts\n ? (hasRunnableStrategy\n ? (hasValidReserve\n ? (hasValidIncomeLayer\n ? (hasValidOptionOverlay\n ? (hasValidDca ? (hasPendingChange ? t(\"readyNote\") : \"\") : t(\"invalidDcaNote\"))\n : t(\"invalidOptionOverlayNote\"))\n : t(\"invalidIncomeLayerNote\"))\n : (executionCashPolicyConflict(state.forms[state.selected])\n ? t(\"invalidExecutionCashPolicyNote\")\n : t(\"invalidReservePolicyNote\")))\n : strategyActionNoteText())\n : t(\"missingConfigNote\")))\n : t(\"readonlyNote\");\n note.classList.toggle(\n \"warning\",\n state.auth.allowed && !loadingConfig && (!hasPrivateAccounts || !hasValidStrategy),\n );\n }\n\n function renderAppVisibility() {\n document.body.classList.toggle(\"app-loading\", !state.appReady);\n el(\"boot-message\").textContent = t(state.bootMessageKey);\n }\n\n function normalizeControlPlanePayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid control plane payload\");\n const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n attention: payload.attention && typeof payload.attention === \"object\" && !Array.isArray(payload.attention)\n ? {\n status: [\"research_only\", \"attention_required\", \"unavailable\"].includes(payload.attention.status)\n ? payload.attention.status\n : \"unavailable\",\n reason_codes: Array.isArray(payload.attention.reason_codes) ? payload.attention.reason_codes : [],\n }\n : { status: \"unavailable\", reason_codes: [\"control_plane_attention_missing\"] },\n candidates: candidates.filter((item) => item && typeof item === \"object\" && item.lifecycle && typeof item.lifecycle === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function normalizeOwnerDecisionQueue(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid owner decision queue\");\n const candidates = Array.isArray(payload.candidates) ? payload.candidates : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n candidates: candidates.filter((entry) => entry && typeof entry === \"object\"\n && entry.candidate && typeof entry.candidate === \"object\"\n && typeof entry.candidate_evidence_sha256 === \"string\"),\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function ownerDecisionEntry(candidateId) {\n return state.ownerDecisions.candidates.find((entry) => entry.candidate?.candidate_id === candidateId) || null;\n }\n\n function ownerDecisionLabel(decision) {\n return {\n approve_limited_live_canary: t(\"ownerDecisionApprove\"),\n keep_parked: t(\"ownerDecisionPark\"),\n retire_candidate: t(\"ownerDecisionRetire\"),\n }[decision] || String(decision || \"—\");\n }\n\n function controlPlaneDataStatusText(status) {\n return status === \"ready\"\n ? t(\"controlDataReady\")\n : (status === \"stale\" ? t(\"controlDataStale\") : t(\"controlDataUnavailable\"));\n }\n\n function controlPlaneAttentionText(attention) {\n const status = attention?.status || \"unavailable\";\n if (status === \"research_only\") return t(\"controlAttentionResearchOnly\");\n if (status === \"attention_required\") return t(\"controlAttentionRequired\");\n return t(\"controlAttentionUnavailable\");\n }\n\n function renderControlPlane() {\n const payload = state.controlPlane.payload;\n const summary = payload.summary || {};\n el(\"control-plane-status\").textContent = `${controlPlaneDataStatusText(payload.data_status)} · ${controlPlaneAttentionText(payload.attention)}`;\n el(\"control-plane-computed-at\").textContent = payload.computed_at\n ? t(\"controlComputedAt\").replace(\"{time}\", new Date(payload.computed_at).toLocaleString())\n : t(\"controlComputedAt\").replace(\"{time}\", \"—\");\n el(\"control-count-candidates\").textContent = String(Number(summary.candidate_count) || 0);\n el(\"control-count-deferred\").textContent = String(Number(summary.deferred) || 0);\n el(\"control-count-parked\").textContent = String(Number(summary.parked) || 0);\n el(\"control-count-owner-decision\").textContent = String(Number(summary.owner_decision_required) || 0);\n\n const notice = el(\"control-plane-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"controlLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"controlStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"controlUnavailableNotice\");\n } else if (payload.attention?.status === \"attention_required\") {\n notice.textContent = t(\"controlAttentionNotice\")\n .replace(\"{deferred}\", String(Number(summary.deferred) || 0))\n .replace(\"{parked}\", String(Number(summary.parked) || 0))\n .replace(\"{signals}\", String(payload.attention.reason_codes?.length || 0));\n } else if (payload.errors?.length) {\n notice.textContent = t(\"controlUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || \"live 仍需所有者明确决定。\";\n }\n\n const list = el(\"control-plane-list\");\n list.replaceChildren();\n if (!payload.candidates.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"controlEmptyCandidates\");\n list.appendChild(empty);\n return;\n }\n for (const item of payload.candidates) {\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = `${item.candidate_kind || \"candidate\"} · ${domainLabel(item.domain || \"\")}`;\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(item.candidate_id || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = item.recommendation?.reason || t(\"controlNoRecommendation\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"controlStageMeta\")\n .replace(\"{stage}\", item.lifecycle?.stage || \"—\")\n .replace(\"{status}\", item.lifecycle?.status || \"unknown\")\n .replace(\"{freshness}\", item.freshness?.status || \"unknown\");\n main.append(meta, title, reason, detail);\n const stateBlock = document.createElement(\"div\");\n stateBlock.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = t(\"controlNext\");\n const stage = document.createElement(\"strong\");\n stage.textContent = item.lifecycle?.stage || \"—\";\n const recommendation = document.createElement(\"small\");\n recommendation.textContent = item.recommendation?.code || \"none\";\n stateBlock.append(label, stage, recommendation);\n const ownerEntry = ownerDecisionEntry(item.candidate_id);\n if (ownerEntry) {\n const ownerDecision = document.createElement(\"div\");\n ownerDecision.className = \"owner-decision\";\n const ownerTitle = document.createElement(\"strong\");\n ownerTitle.textContent = t(\"ownerDecisionTitle\");\n const ownerDetail = document.createElement(\"small\");\n if (ownerEntry.intent) {\n ownerDetail.textContent = t(\"ownerDecisionRecorded\")\n .replace(\"{decision}\", ownerDecisionLabel(ownerEntry.intent.decision));\n } else if (!state.auth.admin) {\n ownerDetail.textContent = t(\"ownerDecisionAdminOnly\");\n } else {\n ownerDetail.textContent = t(\"ownerDecisionReady\");\n }\n ownerDecision.append(ownerTitle, ownerDetail);\n if (!ownerEntry.intent && state.auth.admin) {\n const actions = document.createElement(\"div\");\n actions.className = \"owner-decision__actions\";\n const submitting = state.ownerDecisions.submittingCandidateId === item.candidate_id;\n for (const [decision, labelKey] of [\n [\"approve_limited_live_canary\", \"ownerDecisionApprove\"],\n [\"keep_parked\", \"ownerDecisionPark\"],\n [\"retire_candidate\", \"ownerDecisionRetire\"],\n ]) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"owner-decision__button\";\n button.dataset.ownerDecision = decision;\n button.dataset.candidateId = item.candidate_id;\n button.dataset.candidateEvidenceSha256 = ownerEntry.candidate_evidence_sha256;\n button.disabled = submitting;\n button.textContent = submitting ? t(\"ownerDecisionSubmitting\") : t(labelKey);\n actions.appendChild(button);\n }\n ownerDecision.appendChild(actions);\n }\n main.appendChild(ownerDecision);\n }\n card.append(main, stateBlock);\n list.appendChild(card);\n }\n }\n\n const M0_RESEARCH_DISPLAY_LIMIT = 100;\n\n const m0ResearchLabels = {\n subject: {\n asset_idea: { zh: \"资产观察\", en: \"Asset idea\" },\n theme_context: { zh: \"主题背景\", en: \"Theme context\" },\n strategy_hypothesis: { zh: \"研究假设\", en: \"Research hypothesis\" },\n risk_context: { zh: \"风险背景\", en: \"Risk context\" },\n },\n state: {\n candidate: { zh: \"待验证\", en: \"Candidate\" },\n source_verification_required: { zh: \"等待来源核验\", en: \"Source verification required\" },\n deferred: { zh: \"暂缓\", en: \"Deferred\" },\n context_only: { zh: \"仅作背景\", en: \"Context only\" },\n },\n freshness: {\n fresh: { zh: \"有效\", en: \"Fresh\" },\n stale: { zh: \"已过期\", en: \"Stale\" },\n unknown: { zh: \"未知\", en: \"Unknown\" },\n },\n horizon: {\n short: { zh: \"短期\", en: \"Short\" },\n medium: { zh: \"中期\", en: \"Medium\" },\n long: { zh: \"长期\", en: \"Long\" },\n not_applicable: { zh: \"不适用\", en: \"Not applicable\" },\n },\n confidence: {\n high: { zh: \"高\", en: \"High\" },\n medium: { zh: \"中\", en: \"Medium\" },\n low: { zh: \"低\", en: \"Low\" },\n mixed: { zh: \"混合\", en: \"Mixed\" },\n no_event: { zh: \"无事件\", en: \"No event\" },\n unknown: { zh: \"未知\", en: \"Unknown\" },\n },\n style: {\n event_driven: { zh: \"事件驱动\", en: \"Event driven\" },\n long_horizon_growth: { zh: \"长期成长\", en: \"Long-horizon growth\" },\n value_quality: { zh: \"价值质量\", en: \"Value quality\" },\n macro_context: { zh: \"宏观背景\", en: \"Macro context\" },\n mixed_research: { zh: \"综合研究\", en: \"Mixed research\" },\n },\n conflict: {\n none: { zh: \"无\", en: \"None\" },\n conflict: { zh: \"存在\", en: \"Present\" },\n unavailable: { zh: \"不可用\", en: \"Unavailable\" },\n },\n drift: {\n none: { zh: \"无\", en: \"None\" },\n drift: { zh: \"存在\", en: \"Present\" },\n unavailable: { zh: \"不可用\", en: \"Unavailable\" },\n },\n };\n\n function m0ResearchLabel(group, value) {\n const entry = m0ResearchLabels[group]?.[value];\n return entry ? (state.lang === \"zh\" ? entry.zh : entry.en) : \"—\";\n }\n\n function m0ResearchTimestamp(value) {\n if (typeof value !== \"string\" || !value) return \"—\";\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? \"—\" : parsed.toLocaleString();\n }\n\n function m0ResearchHorizons(values) {\n if (!Array.isArray(values) || !values.length) return \"—\";\n const labels = values.map((value) => m0ResearchLabel(\"horizon\", value)).filter((value) => value !== \"—\");\n return labels.length ? labels.join(\" / \") : \"—\";\n }\n\n function m0ResearchEntries(subjects) {\n if (!Array.isArray(subjects)) return [];\n const entries = [];\n for (const item of subjects) {\n if (!item || typeof item !== \"object\" || !item.subject || typeof item.subject !== \"object\") continue;\n if (!Array.isArray(item.observations)) continue;\n for (const observation of item.observations) {\n if (!observation || typeof observation !== \"object\" || !observation.research_context) continue;\n entries.push({\n subject: item.subject,\n observation,\n horizonConflict: item.horizon_conflict,\n historicalStaleHorizonDrift: item.historical_stale_horizon_drift,\n });\n }\n }\n return entries;\n }\n\n function normalizeM0ResearchPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid M0 research payload\");\n const subjects = Array.isArray(payload.subjects) ? payload.subjects : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n viewed_at: typeof payload.viewed_at === \"string\" ? payload.viewed_at : null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n subjects: subjects.filter((item) => item && typeof item === \"object\"\n && item.subject && typeof item.subject === \"object\" && Array.isArray(item.observations)),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderM0Research() {\n const payload = state.m0Research.payload;\n const entries = m0ResearchEntries(payload.subjects);\n const shownEntries = entries.slice(0, M0_RESEARCH_DISPLAY_LIMIT);\n const notice = el(\"m0-research-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"m0ResearchLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"m0ResearchStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"m0ResearchUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"m0ResearchUpstreamNotice\").replace(\"{count}\", String(payload.errors.length));\n } else {\n const more = entries.length > M0_RESEARCH_DISPLAY_LIMIT\n ? ` ${t(\"m0ResearchMore\").replace(\"{count}\", String(M0_RESEARCH_DISPLAY_LIMIT))}`\n : \"\";\n notice.textContent = `${t(\"m0ResearchNoOrder\")}${more}`;\n }\n\n const list = el(\"m0-research-list\");\n list.replaceChildren();\n if (!shownEntries.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"m0ResearchEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of shownEntries) {\n const subject = entry.subject || {};\n const observation = entry.observation || {};\n const context = observation.research_context || {};\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"m0ResearchMeta\")\n .replace(\"{kind}\", m0ResearchLabel(\"subject\", subject.kind))\n .replace(\"{viewed}\", m0ResearchTimestamp(payload.viewed_at));\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = typeof subject.identifier === \"string\" ? subject.identifier : \"—\";\n const stateFreshness = document.createElement(\"p\");\n stateFreshness.className = \"health-card__reason\";\n stateFreshness.textContent = t(\"m0ResearchStateFreshness\")\n .replace(\"{state}\", m0ResearchLabel(\"state\", context.state))\n .replace(\"{freshness}\", m0ResearchLabel(\"freshness\", observation.freshness?.status));\n const horizons = document.createElement(\"div\");\n horizons.className = \"health-card__meta\";\n horizons.textContent = t(\"m0ResearchHorizons\")\n .replace(\"{primary}\", m0ResearchLabel(\"horizon\", context.primary_horizon))\n .replace(\"{suitable}\", m0ResearchHorizons(context.suitable_horizons));\n const evidence = document.createElement(\"div\");\n evidence.className = \"health-card__meta\";\n evidence.textContent = t(\"m0ResearchEvidence\")\n .replace(\"{confidence}\", m0ResearchLabel(\"confidence\", context.source_confidence))\n .replace(\"{style}\", m0ResearchLabel(\"style\", context.source_style))\n .replace(\"{digest}\", `${shortResearchDigest(observation.source_report_digest)} / ${shortResearchDigest(observation.source_entry_digest)}`);\n const consistency = document.createElement(\"div\");\n consistency.className = \"health-card__meta\";\n consistency.textContent = t(\"m0ResearchConsistency\")\n .replace(\"{conflict}\", m0ResearchLabel(\"conflict\", entry.horizonConflict?.status))\n .replace(\"{drift}\", m0ResearchLabel(\"drift\", entry.historicalStaleHorizonDrift?.status));\n main.append(meta, title, stateFreshness, horizons, evidence, consistency);\n const status = document.createElement(\"div\");\n status.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = \"M0\";\n const freshness = document.createElement(\"strong\");\n freshness.textContent = m0ResearchLabel(\"freshness\", observation.freshness?.status);\n const stateLabel = document.createElement(\"small\");\n stateLabel.textContent = m0ResearchLabel(\"state\", context.state);\n status.append(label, freshness, stateLabel);\n card.append(main, status);\n list.appendChild(card);\n }\n }\n\n function normalizeAdaptiveSelectionPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid adaptive selection payload\");\n const selections = Array.isArray(payload.selections) ? payload.selections : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n selections: selections.filter((item) => item && typeof item === \"object\"\n && typeof item.source_id === \"string\" && item.decision && typeof item.decision === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderAdaptiveSelection() {\n const payload = state.adaptiveSelection.payload;\n const notice = el(\"adaptive-selection-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"adaptiveSelectionLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"adaptiveSelectionStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"adaptiveSelectionUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"adaptiveSelectionUpstreamNotice\").replace(\"{count}\", String(payload.errors.length));\n } else {\n notice.textContent = payload.policy?.notice || t(\"adaptiveSelectionNoOrder\");\n }\n\n const list = el(\"adaptive-selection-list\");\n list.replaceChildren();\n if (!payload.selections.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"adaptiveSelectionEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.selections) {\n const decision = entry.decision || {};\n const context = decision.market_context || {};\n const recommended = decision.candidates?.find((item) => item.accepted\n && item.strategy_profile === decision.recommended_strategy_profile) || null;\n const rejected = (decision.candidates || []).filter((item) => !item.accepted);\n const reasons = (recommended?.reasons?.length ? recommended.reasons : rejected.flatMap((item) => item.reasons || []))\n .slice(0, 3)\n .join(\", \");\n\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"adaptiveSelectionMeta\")\n .replace(\"{source}\", entry.source_id || \"unknown\")\n .replace(\"{domain}\", domainLabel(context.domain || \"\"))\n .replace(\"{asOf}\", context.as_of || \"—\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = recommended\n ? `${t(\"adaptiveSelectionRecommended\")} · ${recommended.strategy_profile}`\n : t(\"adaptiveSelectionNoCandidate\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = reasons\n ? t(\"adaptiveSelectionReason\").replace(\"{reasons}\", reasons)\n : t(\"adaptiveSelectionNoOrder\");\n main.append(meta, title, reason);\n const status = document.createElement(\"div\");\n status.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = \"NO ORDER\";\n const score = document.createElement(\"strong\");\n score.textContent = typeof recommended?.score === \"number\" ? recommended.score.toFixed(3) : \"—\";\n const freshness = document.createElement(\"small\");\n freshness.textContent = entry.freshness?.data_status || \"unknown\";\n status.append(label, score, freshness);\n card.append(main, status);\n list.appendChild(card);\n }\n }\n\n function normalizeExecutionEvidencePayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid execution evidence payload\");\n const deployments = Array.isArray(payload.deployments) ? payload.deployments : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n deployments: deployments.filter((item) => item && typeof item === \"object\" && item.deployment && typeof item.deployment === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderExecutionEvidence() {\n const payload = state.executionEvidence.payload;\n const notice = el(\"execution-evidence-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"executionEvidenceLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"executionEvidenceStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"executionEvidenceUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"executionEvidenceUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || t(\"executionEvidenceNoOrder\");\n }\n\n const list = el(\"execution-evidence-list\");\n list.replaceChildren();\n if (!payload.deployments.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"executionEvidenceEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.deployments) {\n const deployment = entry.deployment || {};\n const strategy = deployment.strategy || {};\n const target = deployment.target || {};\n const capabilities = deployment.capabilities || {};\n const evidence = deployment.evidence || {};\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"executionEvidenceMeta\")\n .replace(\"{platform}\", target.platform || \"unknown\")\n .replace(\"{environment}\", target.environment || \"unknown\")\n .replace(\"{source}\", entry.source_id || \"unknown\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(strategy.candidate_id || deployment.deployment_id || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = deployment.recommendation?.reason_code || t(\"executionEvidenceNoOrder\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"executionEvidenceDetail\")\n .replace(\"{strategy}\", evidence.strategy || \"unknown\")\n .replace(\"{data}\", evidence.target_data || \"unknown\")\n .replace(\"{execution}\", evidence.target_execution || \"unknown\")\n .replace(\"{shadow}\", capabilities.shadow || \"unknown\")\n .replace(\"{paper}\", capabilities.paper || \"unknown\");\n main.append(meta, title, reason, detail);\n const stateBlock = document.createElement(\"div\");\n stateBlock.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = t(\"executionEvidenceNext\");\n const recommendation = document.createElement(\"strong\");\n recommendation.textContent = deployment.recommendation?.code || \"parked\";\n const freshness = document.createElement(\"small\");\n freshness.textContent = entry.freshness?.data_status || \"unknown\";\n stateBlock.append(label, recommendation, freshness);\n card.append(main, stateBlock);\n list.appendChild(card);\n }\n }\n\n function normalizeResearchTaskPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid research task payload\");\n const tasks = Array.isArray(payload.tasks) ? payload.tasks : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n tasks: tasks.filter((item) => item && typeof item === \"object\" && item.task && typeof item.task === \"object\"),\n policy: payload.policy && typeof payload.policy === \"object\" ? payload.policy : {},\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function shortResearchDigest(value) {\n const text = String(value || \"\");\n return /^[0-9a-f]{64}$/.test(text) ? `${text.slice(0, 10)}…` : \"—\";\n }\n\n function renderResearchTasks() {\n const payload = state.researchTasks.payload;\n const notice = el(\"research-task-notice\");\n if (!state.auth.allowed) {\n notice.textContent = t(\"researchTaskLoginNotice\");\n } else if (payload.data_status === \"stale\") {\n notice.textContent = t(\"researchTaskStaleNotice\");\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = t(\"researchTaskUnavailableNotice\");\n } else if (payload.errors?.length) {\n notice.textContent = t(\"researchTaskUpstreamNotice\").replace(\"{count}\", payload.errors.length);\n } else {\n notice.textContent = payload.policy?.notice || t(\"researchTaskNoOrder\");\n }\n\n const list = el(\"research-task-list\");\n list.replaceChildren();\n if (!payload.tasks.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = t(\"researchTaskEmpty\");\n list.appendChild(empty);\n return;\n }\n for (const entry of payload.tasks) {\n const task = entry.task || {};\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = t(\"researchTaskMeta\")\n .replace(\"{type}\", task.task_type || \"research\")\n .replace(\"{domain}\", domainLabel(task.target?.domain || \"\"))\n .replace(\"{created}\", task.created_at ? new Date(task.created_at).toLocaleString() : \"—\");\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = `${task.target?.candidate_id || \"unknown\"} · ${task.task_id || \"unknown\"}`;\n const hypothesis = document.createElement(\"p\");\n hypothesis.className = \"health-card__reason\";\n hypothesis.textContent = task.experiment?.hypothesis || t(\"researchTaskNoOrder\");\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = t(\"researchTaskLimits\")\n .replace(\"{runs}\", String(task.experiment?.max_runs || \"—\"))\n .replace(\"{seconds}\", String(task.experiment?.max_wall_seconds || \"—\"))\n .replace(\"{p1}\", shortResearchDigest(task.evidence?.p1_input_digest))\n .replace(\"{p2}\", shortResearchDigest(task.evidence?.p2_config_digest))\n .replace(\"{p3}\", shortResearchDigest(task.evidence?.p3_evidence_id));\n main.append(meta, title, hypothesis, detail);\n const status = document.createElement(\"div\");\n status.className = \"health-card__score\";\n const label = document.createElement(\"small\");\n label.textContent = \"NO ORDER\";\n const count = document.createElement(\"strong\");\n count.textContent = String(task.experiment?.max_runs || \"—\");\n const source = document.createElement(\"small\");\n source.textContent = entry.freshness?.data_status || \"unknown\";\n status.append(label, count, source);\n card.append(main, status);\n list.appendChild(card);\n }\n }\n\n function healthStatusLabel(status) {\n return { healthy: \"健康\", watch: \"观察\", review: \"复核\", critical: \"严重\" }[status] || \"未知\";\n }\n\n function normalizeHealthPayload(payload) {\n if (!payload || typeof payload !== \"object\" || Array.isArray(payload)) throw new Error(\"invalid health payload\");\n const strategies = Array.isArray(payload.strategies) ? payload.strategies : [];\n return {\n data_status: [\"ready\", \"stale\", \"unavailable\"].includes(payload.data_status) ? payload.data_status : \"unavailable\",\n computed_at: payload.computed_at || null,\n summary: payload.summary && typeof payload.summary === \"object\" ? payload.summary : {},\n strategies: strategies.filter((item) => item && typeof item === \"object\" && [\"healthy\", \"watch\", \"review\", \"critical\"].includes(item.status)),\n errors: Array.isArray(payload.errors) ? payload.errors : [],\n };\n }\n\n function renderHealth() {\n const payload = state.health.payload;\n const summary = payload.summary || {};\n const statusText = payload.data_status === \"ready\"\n ? \"快照已加载\"\n : (payload.data_status === \"stale\" ? \"快照已过期\" : \"等待可用快照\");\n el(\"health-status\").textContent = statusText;\n el(\"health-computed-at\").textContent = payload.computed_at\n ? `最近计算:${new Date(payload.computed_at).toLocaleString()}`\n : \"最近计算:—\";\n el(\"health-count-total\").textContent = String(Number(summary.strategy_count) || 0);\n el(\"health-count-healthy\").textContent = String(Number(summary.healthy) || 0);\n el(\"health-count-watch\").textContent = String(Number(summary.watch) || 0);\n el(\"health-count-review\").textContent = String(Number(summary.review) || 0);\n el(\"health-count-critical\").textContent = String(Number(summary.critical) || 0);\n\n const notice = el(\"health-notice\");\n if (!state.auth.allowed) {\n notice.textContent = \"登录后读取私有策略健康快照;没有快照时不会展示虚构指标。\";\n } else if (payload.data_status === \"stale\") {\n notice.textContent = \"健康快照已超过允许的新鲜度窗口;页面保留原始状态,但不会把它当作当前健康结论。\";\n } else if (payload.data_status !== \"ready\") {\n notice.textContent = \"还没有可用的策略健康快照;当前页面保持 fail-closed 空状态。\";\n } else if (payload.errors?.length) {\n notice.textContent = `快照已加载,但有 ${payload.errors.length} 个上游提示;缺失数据不会被替换成虚构指标。`;\n } else {\n notice.textContent = \"健康不等于已批准 live;正常实盘、资金和杠杆变更仍需人工确认。\";\n }\n\n const list = el(\"health-list\");\n list.replaceChildren();\n const strategies = payload.strategies.filter((item) => state.health.filter === \"all\" || item.status === state.health.filter);\n if (!strategies.length) {\n const empty = document.createElement(\"div\");\n empty.className = \"health-card__empty\";\n empty.textContent = \"暂无可展示的策略健康快照。\";\n list.appendChild(empty);\n return;\n }\n for (const item of strategies) {\n const card = document.createElement(\"article\");\n card.className = \"health-card\";\n const main = document.createElement(\"div\");\n main.className = \"health-card__main\";\n const meta = document.createElement(\"div\");\n meta.className = \"health-card__meta\";\n meta.textContent = `${healthStatusLabel(item.status)} · ${item.domain || \"unknown\"}`;\n const title = document.createElement(\"h4\");\n title.className = \"health-card__title\";\n title.textContent = String(item.profile || \"unknown\");\n const reason = document.createElement(\"p\");\n reason.className = \"health-card__reason\";\n reason.textContent = `${item.decision?.label || \"证据不足,保持研究态\"}。${item.decision?.reason || \"没有可用的机器检查结果。\"}`;\n const detail = document.createElement(\"div\");\n detail.className = \"health-card__meta\";\n detail.textContent = `阶段:${item.review?.requested_stage || \"未标记\"} · 截至:${item.as_of || \"—\"}`;\n main.append(meta, title, reason, detail);\n const scoreBlock = document.createElement(\"div\");\n scoreBlock.className = \"health-card__score\";\n const scoreLabel = document.createElement(\"small\");\n scoreLabel.textContent = \"HEALTH\";\n const score = document.createElement(\"strong\");\n score.textContent = typeof item.score === \"number\" ? item.score.toFixed(1) : \"—\";\n const decision = document.createElement(\"small\");\n decision.textContent = item.decision?.code || \"evidence_missing\";\n scoreBlock.append(scoreLabel, score, decision);\n card.append(main, scoreBlock);\n list.appendChild(card);\n }\n }\n\n function renderConsoleView() {\n const controlButton = el(\"control-plane-view-button\");\n const healthButton = el(\"health-view-button\");\n const switchButton = el(\"switch-view-button\");\n const controlVisible = state.view === \"control\";\n const healthVisible = state.view === \"health\";\n el(\"control-plane-view\").hidden = !controlVisible;\n el(\"health-view\").hidden = !healthVisible;\n el(\"switch-view\").hidden = controlVisible || healthVisible;\n controlButton.classList.toggle(\"active\", controlVisible);\n healthButton.classList.toggle(\"active\", healthVisible);\n switchButton.classList.toggle(\"active\", !controlVisible && !healthVisible);\n }\n\n function render() {\n applyLanguage();\n renderRuntimeAuthorityStatus();\n renderConsoleView();\n renderControlPlane();\n renderM0Research();\n renderAdaptiveSelection();\n renderExecutionEvidence();\n renderResearchTasks();\n renderHealth();\n renderPlatforms();\n renderControls();\n renderSummary();\n renderPlanReadiness();\n renderAuth();\n renderAppVisibility();\n }\n\n async function refreshSession() {\n state.bootMessageKey = \"bootSession\";\n render();\n try {\n const session = await requestJson(\"/api/session\");\n state.auth = {\n available: true,\n allowed: Boolean(session.allowed),\n admin: Boolean(session.admin),\n login: session.login || null,\n };\n } catch {\n state.auth = { available: false, allowed: false, admin: false, login: null };\n }\n if (state.auth.allowed) {\n await refreshControlPlane();\n await refreshOwnerDecisions();\n await refreshM0Research();\n await refreshAdaptiveSelection();\n await refreshExecutionEvidence();\n await refreshResearchTasks();\n await refreshHealth();\n await refreshConfig();\n } else {\n state.bootMessageKey = \"bootPublic\";\n state.appReady = true;\n render();\n }\n }\n\n async function refreshHealth() {\n if (!state.auth.allowed) {\n renderHealth();\n return;\n }\n try {\n state.health.payload = normalizeHealthPayload(await requestJson(\"/api/strategy-health\"));\n } catch {\n state.health.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { strategy_count: 0, healthy: 0, watch: 0, review: 0, critical: 0 },\n strategies: [],\n errors: [\"health_request_failed\"],\n };\n }\n renderHealth();\n }\n\n async function refreshControlPlane() {\n if (!state.auth.allowed) {\n renderControlPlane();\n return;\n }\n try {\n state.controlPlane.payload = normalizeControlPlanePayload(await requestJson(\"/api/control-plane\"));\n } catch {\n state.controlPlane.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { candidate_count: 0, deferred: 0, parked: 0, owner_decision_required: 0 },\n attention: { status: \"unavailable\", reason_codes: [\"control_plane_request_failed\"] },\n candidates: [],\n policy: { p4_p5_automation: \"not_configured\", p6_owner_decision_required: true },\n errors: [\"control_plane_request_failed\"],\n };\n }\n renderControlPlane();\n }\n\n async function refreshOwnerDecisions() {\n if (!state.auth.allowed) {\n state.ownerDecisions = { data_status: \"unavailable\", candidates: [], errors: [] };\n renderControlPlane();\n return;\n }\n try {\n state.ownerDecisions = normalizeOwnerDecisionQueue(await requestJson(\"/api/owner-decisions\"));\n } catch {\n state.ownerDecisions = {\n data_status: \"unavailable\",\n candidates: [],\n errors: [\"owner_decision_queue_request_failed\"],\n };\n }\n renderControlPlane();\n }\n\n async function refreshM0Research() {\n if (!state.auth.allowed) {\n renderM0Research();\n return;\n }\n try {\n state.m0Research.payload = normalizeM0ResearchPayload(await requestJson(\"/api/m0-research\"));\n } catch {\n state.m0Research.payload = {\n data_status: \"unavailable\",\n viewed_at: null,\n summary: {\n subject_count: 0,\n observation_count: 0,\n fresh_observation_count: 0,\n stale_observation_count: 0,\n unknown_observation_count: 0,\n horizon_conflict_count: 0,\n historical_stale_horizon_drift_count: 0,\n },\n subjects: [],\n policy: { authority: \"research_only\", no_order: true, permitted_next_step: \"research_validation_only\" },\n errors: [\"m0_research_request_failed\"],\n };\n }\n renderM0Research();\n }\n\n async function refreshAdaptiveSelection() {\n if (!state.auth.allowed) {\n renderAdaptiveSelection();\n return;\n }\n try {\n state.adaptiveSelection.payload = normalizeAdaptiveSelectionPayload(await requestJson(\"/api/adaptive-selection\"));\n } catch {\n state.adaptiveSelection.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { source_count: 0, decision_count: 0, candidate_count: 0, recommended_count: 0, rejected_candidate_count: 0 },\n selections: [],\n policy: { authority: \"shadow_only\", no_order: true, execution_authority_granted: false },\n errors: [\"adaptive_selection_request_failed\"],\n };\n }\n renderAdaptiveSelection();\n }\n\n async function recordOwnerDecision(button) {\n if (!state.auth.admin) return;\n const candidateId = String(button.dataset.candidateId || \"\");\n const candidateEvidenceSha256 = String(button.dataset.candidateEvidenceSha256 || \"\");\n const decision = String(button.dataset.ownerDecision || \"\");\n if (!candidateId || !candidateEvidenceSha256 || !decision) return;\n if (!window.confirm(t(\"ownerDecisionConfirm\"))) return;\n state.ownerDecisions.submittingCandidateId = candidateId;\n renderControlPlane();\n try {\n const response = await fetch(\"/api/owner-decisions\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n candidate_id: candidateId,\n decision,\n candidate_evidence_sha256: candidateEvidenceSha256,\n }),\n });\n const payload = await response.json();\n if (!response.ok || !payload.ok) throw new Error(payload.error || t(\"ownerDecisionFailed\"));\n showToast(t(\"ownerDecisionSuccess\"), { duration: 7000 });\n await refreshControlPlane();\n await refreshOwnerDecisions();\n } catch (error) {\n showToast(`${t(\"ownerDecisionFailed\")}: ${error.message}`, { duration: 12000 });\n } finally {\n delete state.ownerDecisions.submittingCandidateId;\n renderControlPlane();\n }\n }\n\n async function refreshExecutionEvidence() {\n if (!state.auth.allowed) {\n renderExecutionEvidence();\n return;\n }\n try {\n state.executionEvidence.payload = normalizeExecutionEvidencePayload(await requestJson(\"/api/execution-evidence\"));\n } catch {\n state.executionEvidence.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { deployment_count: 0, autonomous_shadow: 0, autonomous_paper: 0, owner_canary_decision: 0, parked: 0 },\n deployments: [],\n policy: { execution_evidence_read_only: true, p6_owner_decision_required: true, limited_live_canary_active: false },\n errors: [\"execution_evidence_request_failed\"],\n };\n }\n renderExecutionEvidence();\n }\n\n async function refreshResearchTasks() {\n if (!state.auth.allowed) {\n renderResearchTasks();\n return;\n }\n try {\n state.researchTasks.payload = normalizeResearchTaskPayload(await requestJson(\"/api/research-tasks\"));\n } catch {\n state.researchTasks.payload = {\n data_status: \"unavailable\",\n computed_at: null,\n summary: { task_count: 0 },\n tasks: [],\n policy: { research_only: true, no_order: true, size_zero_required: true, p4_p5_p6_authorized: false },\n errors: [\"research_task_request_failed\"],\n };\n }\n renderResearchTasks();\n }\n\n async function refreshStrategyProfiles() {\n state.bootMessageKey = \"bootStrategy\";\n render();\n try {\n const payload = await requestJson(\"/api/strategy-profiles\");\n applyStrategyProfiles(payload.strategyProfiles || []);\n if (payload.platformMeta) platformMeta = payload.platformMeta;\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n render();\n } catch {\n applyStrategyProfiles(defaultStrategyProfiles);\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n }\n }\n\n async function refreshConfig() {\n if (!state.auth.available || !state.auth.allowed) return;\n state.configSource = \"loading\";\n state.bootMessageKey = \"bootConfig\";\n render();\n try {\n const payload = await requestJson(\"/api/config\");\n if (payload.accountOptions) {\n applyStrategyProfiles(payload.strategyProfiles || defaultStrategyProfiles);\n state.accountOptions = normalizeAccountOptions(payload.accountOptions);\n if (payload.platformMeta) platformMeta = payload.platformMeta;\n state.repositories = normalizePlatformRepositories(payload.platformRepositories || {});\n state.currentStrategies = normalizeCurrentStrategies(payload.currentStrategies || {});\n state.configSource = \"private\";\n for (const platform of Object.keys(platformMeta)) {\n ensureAccountSelection(platform);\n syncStrategyForAccount(platform);\n }\n } else {\n state.configSource = \"default\";\n state.currentStrategies = {};\n }\n } catch (error) {\n state.configSource = \"default\";\n state.currentStrategies = {};\n if (isRequestTimeoutError(error)) {\n state.bootMessageKey = \"bootTimeout\";\n } else {\n state.bootMessageKey = \"bootPublic\";\n }\n } finally {\n state.appReady = true;\n render();\n }\n }\n\n function normalizeAccountOptions(raw) {\n const normalized = clone(defaultAccountOptions);\n for (const platform of Object.keys(platformMeta)) {\n if (!Array.isArray(raw[platform]) || !raw[platform].length) continue;\n normalized[platform] = raw[platform].map((item, index) => ({\n key: String(item.key || item.target_name || index),\n label: String(item.label || item.target_name || item.key || platform),\n target_name: String(item.target_name || item.key || \"\"),\n account_selector: item.account_selector ? String(item.account_selector) : \"\",\n deployment_selector: item.deployment_selector ? String(item.deployment_selector) : \"\",\n account_scope: item.account_scope ? String(item.account_scope) : \"\",\n service_name: item.service_name ? String(item.service_name) : \"\",\n cash_currency: item.cash_currency || item.market_currency || item.trading_currency\n ? String(item.cash_currency || item.market_currency || item.trading_currency).trim().toUpperCase()\n : \"\",\n supported_domains: normalizeSupportedDomains(platform, item),\n github_environment: item.github_environment ? String(item.github_environment) : \"\",\n variable_scope: item.variable_scope ? String(item.variable_scope) : \"\",\n plugin_mode: item.plugin_mode ? String(item.plugin_mode) : \"\",\n option_overlay_mode: item.option_overlay_mode ? normalizeOptionOverlayMode(item.option_overlay_mode) : \"\",\n cash_only_execution_mode: item.cash_only_execution_mode\n ? normalizeCashOnlyExecutionMode(item.cash_only_execution_mode)\n : \"\",\n dca_mode: item.dca_mode ? normalizeDcaMode(item.dca_mode) : \"\",\n dca_base_investment_usd: cleanDisplayPositiveNumber(item.dca_base_investment_usd),\n }));\n }\n return normalized;\n }\n\n function normalizeSupportedDomains(platform, item) {\n const raw = Array.isArray(item?.supported_domains)\n ? item.supported_domains\n : String(item?.supported_domains || \"\").split(/[\\s,;]+/);\n const cleaned = raw.map(cleanStrategyDomain).filter(Boolean);\n if (cleaned.length) return [...new Set(cleaned)];\n return inferSupportedDomains(platform, item || {});\n }\n\n function normalizeCurrentStrategies(raw) {\n const normalized = {};\n for (const platform of Object.keys(platformMeta)) {\n if (!raw[platform] || typeof raw[platform] !== \"object\" || Array.isArray(raw[platform])) continue;\n normalized[platform] = {};\n for (const [key, entry] of Object.entries(raw[platform])) {\n const profile = cleanStrategyProfile(entry?.strategy_profile);\n const minReservedCashUsd = cleanDisplayNumber(entry?.min_reserved_cash_usd ?? entry?.reserved_cash_floor_usd);\n const reservedCashRatio = cleanDisplayRatio(entry?.reserved_cash_ratio);\n const incomeLayerEnabled = cleanOptionalBoolean(entry?.income_layer_enabled);\n const incomeLayerStartUsd = cleanDisplayNumber(entry?.income_layer_start_usd);\n const incomeLayerMaxRatio = cleanDisplayRatio(entry?.income_layer_max_ratio);\n const optionOverlayEnabled = cleanOptionalBoolean(entry?.option_overlay_enabled);\n const cashOnlyExecution = cleanOptionalBoolean(entry?.cash_only_execution);\n const runtimeTargetEnabled = cleanOptionalBoolean(entry?.runtime_target_enabled);\n const dcaMode = entry?.dca_mode ? normalizeDcaMode(entry.dca_mode) : \"\";\n const dcaBaseInvestmentUsd = cleanDisplayPositiveNumber(entry?.dca_base_investment_usd);\n const executionMode = normalizeExecutionMode(entry?.execution_mode, entry?.dry_run_only);\n if (\n !profile &&\n !minReservedCashUsd &&\n !reservedCashRatio &&\n incomeLayerEnabled === null &&\n !incomeLayerStartUsd &&\n !incomeLayerMaxRatio &&\n optionOverlayEnabled === null &&\n cashOnlyExecution === null &&\n runtimeTargetEnabled === null &&\n !dcaMode &&\n !dcaBaseInvestmentUsd &&\n !executionMode\n ) continue;\n normalized[platform][String(key)] = {\n strategy_profile: profile,\n execution_mode: executionMode,\n dry_run_only: entry?.dry_run_only === true || entry?.dry_run_only === \"true\" || entry?.dry_run_only === \"1\",\n min_reserved_cash_usd: minReservedCashUsd,\n reserved_cash_ratio: reservedCashRatio,\n income_layer_enabled: incomeLayerEnabled,\n income_layer_start_usd: incomeLayerStartUsd,\n income_layer_max_ratio: incomeLayerMaxRatio,\n option_overlay_enabled: optionOverlayEnabled,\n cash_only_execution: cashOnlyExecution,\n runtime_target_enabled: runtimeTargetEnabled,\n dca_mode: dcaMode,\n dca_base_investment_usd: dcaBaseInvestmentUsd,\n source: entry?.source ? String(entry.source) : \"\",\n };\n }\n if (!Object.keys(normalized[platform]).length) delete normalized[platform];\n }\n return normalized;\n }\n\n function normalizePlatformRepositories(raw) {\n const normalized = clone(defaultRepositories);\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return normalized;\n for (const platform of Object.keys(platformMeta)) {\n const repository = String(raw[platform] || \"\").trim();\n if (/^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/.test(repository)) {\n normalized[platform] = repository;\n }\n }\n return normalized;\n }\n\n async function dispatchSwitch() {\n if (!state.auth.allowed) return;\n showToast(t(\"dispatching\"), { duration: 0 });\n try {\n const response = await fetch(\"/api/switch\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(buildInputs()),\n });\n const payload = await response.json();\n if (!response.ok || !payload.ok) throw new Error(payload.error || t(\"dispatchFailed\"));\n showToast(t(\"dispatched\"), { duration: 4000 });\n if (payload.actions_url) window.open(payload.actions_url, \"_blank\", \"noopener,noreferrer\");\n await refreshConfig();\n } catch (error) {\n showToast(`${t(\"dispatchFailed\")}: ${error.message}`, { duration: 12000 });\n }\n }\n\n async function handleLogout() {\n await fetch(\"/api/logout\", { method: \"POST\" });\n window.location.reload();\n }\n\n function summaryText() {\n const inputs = buildInputs();\n return summaryRows(inputs).map(([label, value]) => `${label}: ${value}`).join(\"\\\n\");\n }\n\n document.querySelectorAll(\"[data-view]\").forEach((button) => button.addEventListener(\"click\", () => {\n state.view = [\"control\", \"health\", \"switch\"].includes(button.dataset.view) ? button.dataset.view : \"control\";\n renderConsoleView();\n if (state.view === \"control\") {\n refreshControlPlane();\n refreshOwnerDecisions();\n refreshM0Research();\n refreshResearchTasks();\n }\n if (state.view === \"health\") refreshHealth();\n }));\n\n document.querySelectorAll(\"[data-health-filter]\").forEach((button) => button.addEventListener(\"click\", () => {\n document.querySelectorAll(\"[data-health-filter]\").forEach((node) => node.classList.remove(\"active\"));\n button.classList.add(\"active\");\n state.health.filter = button.dataset.healthFilter;\n renderHealth();\n }));\n\n el(\"control-plane-list\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-owner-decision]\");\n if (!button || button.disabled) return;\n recordOwnerDecision(button);\n });\n\n el(\"platform-strip\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-platform]\");\n if (!button) return;\n state.selected = button.dataset.platform;\n state.forms[state.selected].strategyTouched = false;\n render();\n });\n\n el(\"account-select\").addEventListener(\"change\", () => {\n state.forms[state.selected].accountKey = el(\"account-select\").value;\n state.forms[state.selected].runtimeTargetTouched = false;\n state.forms[state.selected].reservedCashTouched = false;\n state.forms[state.selected].incomeLayerTouched = false;\n state.forms[state.selected].optionOverlayTouched = false;\n state.forms[state.selected].cashOnlyExecutionTouched = false;\n state.forms[state.selected].dcaTouched = false;\n state.forms[state.selected].strategyTouched = false;\n syncStrategyForAccount(state.selected);\n render();\n });\n\n el(\"strategy-select\").addEventListener(\"change\", () => {\n state.forms[state.selected].strategy = el(\"strategy-select\").value;\n state.forms[state.selected].strategyTouched = true;\n state.forms[state.selected].incomeLayerTouched = false;\n state.forms[state.selected].optionOverlayTouched = false;\n state.forms[state.selected].dcaTouched = false;\n syncIncomeLayerForAccount(state.selected);\n syncOptionOverlayForAccount(state.selected);\n syncDcaForAccount(state.selected);\n render();\n });\n\n el(\"mode-control\").addEventListener(\"click\", (event) => {\n const button = event.target.closest(\"[data-mode]\");\n if (!button || button.disabled) return;\n if (!supportedExecutionModesForPlatform(state.selected).includes(button.dataset.mode)) return;\n state.forms[state.selected].executionMode = button.dataset.mode;\n render();\n });\n\n el(\"plugin-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.pluginMode = normalizePluginMode(el(\"plugin-mode-select\").value);\n render();\n });\n\n el(\"runtime-target-enabled-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.runtimeTargetMode = normalizeRuntimeTargetMode(el(\"runtime-target-enabled-select\").value);\n form.runtimeTargetTouched = form.runtimeTargetMode !== \"current\";\n render();\n });\n\n el(\"income-layer-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerMode = normalizeIncomeLayerMode(el(\"income-layer-mode-select\").value);\n form.incomeLayerTouched = form.incomeLayerMode !== \"current\";\n if (form.incomeLayerMode === \"current\") {\n form.incomeLayerTouched = false;\n syncIncomeLayerForAccount(state.selected);\n }\n render();\n });\n\n el(\"income-layer-start-usd-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerTouched = true;\n form.incomeLayerMode = \"enabled\";\n form.incomeLayerStartUsd = el(\"income-layer-start-usd-input\").value.trim();\n render();\n });\n\n el(\"income-layer-max-ratio-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.incomeLayerTouched = true;\n form.incomeLayerMode = \"enabled\";\n form.incomeLayerMaxRatio = el(\"income-layer-max-ratio-input\").value.trim();\n render();\n });\n\n el(\"option-overlay-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.optionOverlayMode = normalizeOptionOverlayMode(el(\"option-overlay-mode-select\").value);\n form.optionOverlayTouched = form.optionOverlayMode !== \"current\";\n render();\n });\n\n el(\"cash-only-execution-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.cashOnlyExecutionMode = normalizeCashOnlyExecutionMode(el(\"cash-only-execution-mode-select\").value);\n form.cashOnlyExecutionTouched = form.cashOnlyExecutionMode !== \"current\";\n if (allowMarginExplicitlySelected(form)) reconcileExecutionCashPolicy(form, \"margin\");\n else restoreReserveAfterMarginDisabled(form);\n render();\n });\n\n el(\"dca-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.dcaTouched = true;\n form.dcaMode = normalizeDcaMode(el(\"dca-mode-select\").value);\n render();\n });\n\n el(\"dca-base-investment-usd-input\").addEventListener(\"input\", () => {\n const form = state.forms[state.selected];\n form.dcaTouched = true;\n form.dcaBaseInvestmentUsd = el(\"dca-base-investment-usd-input\").value.trim();\n render();\n });\n\n el(\"reserve-policy-mode-select\").addEventListener(\"change\", () => {\n const form = state.forms[state.selected];\n form.reservePolicyMode = normalizeReservePolicyMode(el(\"reserve-policy-mode-select\").value);\n form.reservedCashTouched = form.reservePolicyMode !== \"current\";\n if (form.reservePolicyMode === \"current\") syncReservePolicyForAccount(state.selected);\n reconcileExecutionCashPolicy(form, \"reserve\");\n render();\n });\n\n el(\"min-reserved-cash-input\").addEventListener(\"input\", () => {\n state.forms[state.selected].reservedCashTouched = true;\n state.forms[state.selected].minReservedCashUsd = el(\"min-reserved-cash-input\").value.trim();\n render();\n });\n\n el(\"reserved-cash-ratio-input\").addEventListener(\"input\", () => {\n state.forms[state.selected].reservedCashTouched = true;\n state.forms[state.selected].reservedCashRatio = el(\"reserved-cash-ratio-input\").value.trim();\n render();\n });\n\n el(\"copy-button\").addEventListener(\"click\", async () => {\n try {\n await navigator.clipboard.writeText(summaryText());\n showToast(t(\"copied\"), { duration: 3000 });\n } catch {\n showToast(summaryText(), { duration: 0 });\n }\n });\n\n el(\"dispatch-button\").addEventListener(\"click\", dispatchSwitch);\n el(\"logout-button\").addEventListener(\"click\", handleLogout);\n el(\"lang-button\").addEventListener(\"click\", () => {\n state.lang = state.lang === \"zh\" ? \"en\" : \"zh\";\n localStorage.setItem(\"qsl-switch-lang\", state.lang);\n render();\n });\n\n applyStrategyProfiles(defaultStrategyProfiles);\n for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);\n render();\n boot();\n\n async function boot() {\n try {\n await refreshStrategyProfiles();\n await refreshSession();\n } catch {\n state.auth = { available: false, allowed: false, admin: false, login: null };\n state.configSource = \"default\";\n state.currentStrategies = {};\n state.bootMessageKey = \"bootTimeout\";\n state.appReady = true;\n render();\n }\n }\n"; diff --git a/web/strategy-switch-console/index.html b/web/strategy-switch-console/index.html index ceed7cc..766c471 100644 --- a/web/strategy-switch-console/index.html +++ b/web/strategy-switch-console/index.html @@ -78,6 +78,11 @@

只处理需要

需要跟进

+
+ 按需查看 M0 研究观察 +
登录后读取已验证 M0 研究台账;仅研究、无订单。
+
+
按需查看 M1 Shadow 建议
登录后读取已验证的 Shadow 建议;它始终不下单。
diff --git a/web/strategy-switch-console/page_asset.js b/web/strategy-switch-console/page_asset.js index c10ac32..5e37b9b 100644 --- a/web/strategy-switch-console/page_asset.js +++ b/web/strategy-switch-console/page_asset.js @@ -1,2 +1,2 @@ // Generated by scripts/sync_strategy_switch_page_asset.py; do not edit by hand. -export const PAGE_HTML = "\n\n\n \n \n \n QuantStrategyLab 控制台\n \n \n\n\n\n\n\n
\n
\n

QuantStrategyLab

\n

已验证证据、运行许可和所有者决策。

\n
\n
\n \n \n \n \n
\n
\n\n
\n
\n 初始化控制台\n

读取策略配置

\n

正在读取登录状态、账号配置和当前状态。

\n
\n
\n
\n\n
\n \n \n\n
\n
\n
\n 今日待处理 / 只读\n

只处理需要你决定的事。

\n

这里不是交易大屏:只显示经验证的待办、证据是否足够,以及需要人工做出的边界决定。

\n
\n
\n 等待登录\n 最近计算:—\n
\n
\n
登录后读取私有全局快照;页面不会授予实盘权限。
\n
\n
候选总数0
\n
本次延期0
\n
已停车0
\n
等待我决定0
\n
\n

需要跟进

\n
\n
\n 按需查看 M1 Shadow 建议\n
登录后读取已验证的 Shadow 建议;它始终不下单。
\n
\n
\n
\n 按需查看策略 × 平台证据覆盖\n
登录后读取已验证执行证据;页面不会推断 paper 或 live 状态。
\n
\n
\n
\n 按需查看研究任务队列\n
登录后读取已验证研究任务;不会运行或调参。
\n
\n
\n
\n\n \n\n \n
\n\n \n\n\n"; +export const PAGE_HTML = "\n\n\n \n \n \n QuantStrategyLab 控制台\n \n \n\n\n\n\n\n
\n
\n

QuantStrategyLab

\n

已验证证据、运行许可和所有者决策。

\n
\n
\n \n \n \n \n
\n
\n\n
\n
\n 初始化控制台\n

读取策略配置

\n

正在读取登录状态、账号配置和当前状态。

\n
\n
\n
\n\n
\n \n \n\n
\n
\n
\n 今日待处理 / 只读\n

只处理需要你决定的事。

\n

这里不是交易大屏:只显示经验证的待办、证据是否足够,以及需要人工做出的边界决定。

\n
\n
\n 等待登录\n 最近计算:—\n
\n
\n
登录后读取私有全局快照;页面不会授予实盘权限。
\n
\n
候选总数0
\n
本次延期0
\n
已停车0
\n
等待我决定0
\n
\n

需要跟进

\n
\n
\n 按需查看 M0 研究观察\n
登录后读取已验证 M0 研究台账;仅研究、无订单。
\n
\n
\n
\n 按需查看 M1 Shadow 建议\n
登录后读取已验证的 Shadow 建议;它始终不下单。
\n
\n
\n
\n 按需查看策略 × 平台证据覆盖\n
登录后读取已验证执行证据;页面不会推断 paper 或 live 状态。
\n
\n
\n
\n 按需查看研究任务队列\n
登录后读取已验证研究任务;不会运行或调参。
\n
\n
\n
\n\n \n\n \n
\n\n \n\n\n";