Skip to content

Commit 04f9726

Browse files
Pigbibicodex
andcommitted
feat: add read-only lifecycle matrix view
Co-Authored-By: Codex <noreply@openai.com>
1 parent 025d129 commit 04f9726

4 files changed

Lines changed: 117 additions & 1 deletion

File tree

web/strategy-switch-console/app.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,42 @@
158158
gap: 18px;
159159
}
160160

161+
.lifecycle-view {
162+
display: grid;
163+
gap: 18px;
164+
}
165+
166+
.lifecycle-table-wrap {
167+
overflow-x: auto;
168+
border: 1px solid var(--line);
169+
border-radius: 8px;
170+
background: var(--surface);
171+
box-shadow: 0 10px 26px rgba(22, 25, 31, 0.05);
172+
}
173+
174+
.lifecycle-table {
175+
width: 100%;
176+
min-width: 980px;
177+
border-collapse: collapse;
178+
font-size: 12px;
179+
}
180+
181+
.lifecycle-table th,
182+
.lifecycle-table td {
183+
padding: 12px 10px;
184+
border-bottom: 1px solid var(--line);
185+
text-align: left;
186+
vertical-align: top;
187+
white-space: nowrap;
188+
}
189+
190+
.lifecycle-table thead th { color: var(--muted); font-size: 11px; font-weight: 760; background: var(--soft); }
191+
.lifecycle-table tbody th { max-width: 220px; white-space: normal; font-weight: 720; }
192+
.lifecycle-table tbody tr:last-child th,
193+
.lifecycle-table tbody tr:last-child td { border-bottom: 0; }
194+
.lifecycle-parked, .lifecycle-deferred { color: var(--danger); }
195+
.lifecycle-not_started { color: var(--muted); }
196+
161197
.health-head,
162198
.health-board-head {
163199
display: flex;

web/strategy-switch-console/app.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,11 @@
816816
appTitle: "策略切换",
817817
appSubtitle: "选平台、目标账号和策略,一次执行完成切换。",
818818
healthView: "策略健康",
819+
lifecycleView: "生命周期",
819820
switchView: "实盘切换",
821+
lifecycleEyebrow: "生命周期矩阵 / 只读",
822+
lifecycleTitle: "每个策略目前走到哪一步。",
823+
lifecycleSubtitle: "这是脱敏研究状态摘要,不授予 promotion、live 或交易权限。",
820824
healthEyebrow: "策略健康 / 只读",
821825
healthTitle: "先看机器结论,再决定动作。",
822826
healthSubtitle: "健康不等于已批准 live;正常实盘、资金和杠杆变更仍需人工确认。",
@@ -991,7 +995,11 @@
991995
appTitle: "Strategy Switch",
992996
appSubtitle: "Pick platform, target account, and strategy. One action switches everything.",
993997
healthView: "Strategy Health",
998+
lifecycleView: "Lifecycle",
994999
switchView: "Live Switch",
1000+
lifecycleEyebrow: "Lifecycle matrix / read only",
1001+
lifecycleTitle: "See where each strategy currently stands.",
1002+
lifecycleSubtitle: "A redacted research summary; it grants no promotion, live, or trading permission.",
9951003
healthEyebrow: "Strategy health / read only",
9961004
healthTitle: "Read the machine conclusion before choosing an action.",
9971005
healthSubtitle: "Health does not approve live; normal live, funding, and leverage changes still need a human.",
@@ -1194,6 +1202,7 @@
11941202
selected: "longbridge",
11951203
lang: initialLang,
11961204
view: "health",
1205+
lifecycle: { payload: null },
11971206
appReady: false,
11981207
bootMessageKey: "bootMessage",
11991208
auth: { available: false, allowed: false, admin: false, login: null },
@@ -3333,18 +3342,48 @@
33333342

33343343
function renderConsoleView() {
33353344
const healthButton = el("health-view-button");
3345+
const lifecycleButton = el("lifecycle-view-button");
33363346
const switchButton = el("switch-view-button");
33373347
const healthVisible = state.view === "health";
3348+
const lifecycleVisible = state.view === "lifecycle";
33383349
el("health-view").hidden = !healthVisible;
3350+
el("lifecycle-view").hidden = !lifecycleVisible;
33393351
el("switch-view").hidden = healthVisible;
33403352
healthButton.classList.toggle("active", healthVisible);
3353+
lifecycleButton.classList.toggle("active", lifecycleVisible);
33413354
switchButton.classList.toggle("active", !healthVisible);
33423355
}
33433356

3357+
function renderLifecycle() {
3358+
const payload = state.lifecycle.payload;
3359+
const status = el("lifecycle-status");
3360+
const list = el("lifecycle-list");
3361+
list.replaceChildren();
3362+
if (!payload || !Array.isArray(payload.entries)) {
3363+
status.textContent = "不可用";
3364+
el("lifecycle-notice").textContent = "没有可用矩阵快照;当前保持 fail-closed。";
3365+
return;
3366+
}
3367+
status.textContent = "快照已加载";
3368+
el("lifecycle-generated-at").textContent = `生成时间:${payload.generated_at || "—"}`;
3369+
el("lifecycle-notice").textContent = payload.source_policy || "只读研究状态;不授予 promotion、live 或交易权限。";
3370+
for (const entry of payload.entries) {
3371+
const row = document.createElement("tr");
3372+
const name = document.createElement("th"); name.scope = "row"; name.textContent = entry.display_name || entry.id || "unknown";
3373+
row.appendChild(name);
3374+
for (const value of [entry.lineage, entry.p0, entry.p1, entry.p2, entry.p3, entry.p4, entry.p5, entry.p6]) {
3375+
const cell = document.createElement("td"); cell.textContent = value || "—"; if (["parked", "deferred", "not_started"].includes(value)) cell.className = `lifecycle-${value}`; row.appendChild(cell);
3376+
}
3377+
const next = document.createElement("td"); next.textContent = entry.next_action || "—"; row.appendChild(next);
3378+
list.appendChild(row);
3379+
}
3380+
}
3381+
33443382
function render() {
33453383
applyLanguage();
33463384
renderConsoleView();
33473385
renderHealth();
3386+
renderLifecycle();
33483387
renderPlatforms();
33493388
renderControls();
33503389
renderSummary();
@@ -3395,6 +3434,19 @@
33953434
renderHealth();
33963435
}
33973436

3437+
async function refreshLifecycle() {
3438+
try {
3439+
const response = await fetch("/lifecycle-matrix.json", { cache: "no-store" });
3440+
if (!response.ok) throw new Error("lifecycle_matrix_unavailable");
3441+
const payload = await response.json();
3442+
if (payload?.schema_version !== "strategy_lifecycle_matrix.v1" || !Array.isArray(payload.entries)) throw new Error("invalid_lifecycle_matrix");
3443+
state.lifecycle.payload = payload;
3444+
} catch {
3445+
state.lifecycle.payload = null;
3446+
}
3447+
renderLifecycle();
3448+
}
3449+
33983450
async function refreshStrategyProfiles() {
33993451
state.bootMessageKey = "bootStrategy";
34003452
render();
@@ -3582,9 +3634,10 @@
35823634
}
35833635

35843636
document.querySelectorAll("[data-view]").forEach((button) => button.addEventListener("click", () => {
3585-
state.view = button.dataset.view === "switch" ? "switch" : "health";
3637+
state.view = ["switch", "lifecycle"].includes(button.dataset.view) ? button.dataset.view : "health";
35863638
renderConsoleView();
35873639
if (state.view === "health") refreshHealth();
3640+
if (state.view === "lifecycle") refreshLifecycle();
35883641
}));
35893642

35903643
document.querySelectorAll("[data-health-filter]").forEach((button) => button.addEventListener("click", () => {
@@ -3746,6 +3799,7 @@
37463799
applyStrategyProfiles(defaultStrategyProfiles);
37473800
for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);
37483801
render();
3802+
refreshLifecycle();
37493803
boot();
37503804

37513805
async function boot() {

web/strategy-switch-console/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ <h2 data-i18n="bootTitle">读取策略配置</h2>
4545
<main class="shell" id="app-shell">
4646
<nav class="console-nav" aria-label="控制中心视图">
4747
<button class="console-nav__button active" id="health-view-button" type="button" data-view="health" data-i18n="healthView">策略健康</button>
48+
<button class="console-nav__button" id="lifecycle-view-button" type="button" data-view="lifecycle" data-i18n="lifecycleView">生命周期</button>
4849
<button class="console-nav__button" id="switch-view-button" type="button" data-view="switch" data-i18n="switchView">实盘切换</button>
4950
</nav>
5051

@@ -81,6 +82,19 @@ <h3 data-i18n="healthBoard">策略状态</h3>
8182
<div class="health-list" id="health-list"></div>
8283
</section>
8384

85+
<section class="lifecycle-view" id="lifecycle-view" aria-labelledby="lifecycle-view-title" hidden>
86+
<div class="health-head">
87+
<div>
88+
<span class="eyebrow" data-i18n="lifecycleEyebrow">生命周期矩阵 / 只读</span>
89+
<h2 id="lifecycle-view-title" data-i18n="lifecycleTitle">每个策略目前走到哪一步。</h2>
90+
<p data-i18n="lifecycleSubtitle">这是脱敏研究状态摘要,不授予 promotion、live 或交易权限。</p>
91+
</div>
92+
<div class="health-meta"><span id="lifecycle-status" class="pill">读取中</span><span id="lifecycle-generated-at">生成时间:—</span></div>
93+
</div>
94+
<div id="lifecycle-notice" class="health-notice">状态只读;缺失数据不会被推断为已完成。</div>
95+
<div class="lifecycle-table-wrap"><table class="lifecycle-table"><thead><tr><th>策略</th><th>血缘</th><th>P0</th><th>P1</th><th>P2</th><th>P3</th><th>P4</th><th>P5</th><th>P6</th><th>下一步</th></tr></thead><tbody id="lifecycle-list"></tbody></table></div>
96+
</section>
97+
8498
<section id="switch-view" hidden>
8599
<nav class="platform-strip" id="platform-strip" aria-label="Platforms"></nav>
86100

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"schema_version": "strategy_lifecycle_matrix.v1",
3+
"generated_at": "2026-08-23",
4+
"source_policy": "Read-only inventory; statuses never authorize promotion or trading.",
5+
"entries": [
6+
{"id":"tqqq_core_only_p2_v2","display_name":"TQQQ Core Only / historical frozen","lineage":"p2_v2","p0":"verified","p1":"verified","p2":"verified","p3":"parked","p4":"deferred","p5":"deferred","p6":"deferred","next_action":"保持归档,不绕过 fail-fast。"},
7+
{"id":"tqqq_core_only_p2_v5","display_name":"TQQQ Core Only / successor","lineage":"p2_v5","p0":"verified","p1":"verified","p2":"verified","p3":"verified","p4":"in_progress","p5":"in_progress","p6":"not_started","next_action":"接入真实 P1 后运行 shadow/forward observation。"},
8+
{"id":"soxl_core_only","display_name":"SOXL Core Only","lineage":"successor","p0":"in_progress","p1":"in_progress","p2":"in_progress","p3":"in_progress","p4":"not_started","p5":"not_started","p6":"not_started","next_action":"复用 TQQQ successor 契约补齐 P0-P3。"},
9+
{"id":"nasdaq_sp500_smart_dca","display_name":"Nasdaq/S&P 500 Smart DCA","lineage":"smart_dca","p0":"verified","p1":"verified","p2":"verified","p3":"in_progress","p4":"deferred","p5":"deferred","p6":"deferred","next_action":"注册统一 P3 evidence package。"},
10+
{"id":"tecl_xlk_trend_income","display_name":"TECL/XLK Trend Income","lineage":"research_only","p0":"verified","p1":"in_progress","p2":"parked","p3":"parked","p4":"deferred","p5":"deferred","p6":"deferred","next_action":"保持 research-only。"}
11+
]
12+
}

0 commit comments

Comments
 (0)