fix: clarify code queue split-brain liveness

This commit is contained in:
Codex
2026-05-20 01:41:16 +00:00
parent b01907739c
commit ada6da3da6
11 changed files with 264 additions and 10 deletions
+33
View File
@@ -2176,6 +2176,9 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); }
.codex-trace-status-chip.liveness.warn {
border-color: rgba(215, 161, 58, 0.55);
color: #ffe0a2;
background:
linear-gradient(135deg, rgba(215, 161, 58, 0.13), rgba(78, 183, 168, 0.06)),
rgba(0,0,0,0.18);
}
.codex-trace-status-chip.liveness.failed {
border-color: rgba(255, 98, 98, 0.58);
@@ -2229,6 +2232,36 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); }
font-size: 11px;
overflow-wrap: anywhere;
}
.codex-liveness-advisory {
display: flex;
flex-wrap: wrap;
gap: 6px;
align-items: center;
min-width: 0;
padding: 7px 8px;
border: 1px solid rgba(78, 183, 168, 0.28);
background: rgba(78, 183, 168, 0.07);
color: var(--muted);
}
.codex-liveness-advisory b {
color: var(--accent-2);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.codex-liveness-advisory span {
min-width: 0;
overflow-wrap: anywhere;
}
.codex-liveness-advisory.warn {
border-color: rgba(215, 161, 58, 0.42);
background: rgba(215, 161, 58, 0.07);
}
.codex-liveness-advisory.warn b { color: var(--warn); }
.codex-liveness-advisory.failed {
border-color: rgba(255, 98, 98, 0.46);
background: rgba(207, 106, 84, 0.09);
}
.codex-liveness-advisory.failed b { color: #ffb2b2; }
.codex-liveness-reasons {
display: flex;
flex-wrap: wrap;
+54 -6
View File
@@ -239,10 +239,39 @@ function executionDiagnosticsFromQueue(queue: any, health: any): AnyRecord {
|| {};
}
function diagnosticsTone(state: any): string {
const value = String(state || "unknown").toLowerCase();
function diagnosticsHeartbeatRiskTaskIds(diagnostics: any): string[] {
return Array.from(new Set([
...stringArray(diagnostics?.heartbeatRiskTaskIds),
...stringArray(diagnostics?.heartbeatExpiredTaskIds),
...stringArray(diagnostics?.heartbeatMissingTaskIds),
...stringArray(diagnostics?.staleRecoveryCandidateTaskIds),
])).sort();
}
function splitBrainLiveDiagnostics(diagnostics: any): boolean {
if (typeof diagnostics?.splitBrainLive === "boolean") return diagnostics.splitBrainLive;
const state = String(diagnostics?.state || diagnostics?.health || "").toLowerCase();
return state === "split-brain"
&& stringArray(diagnostics?.heartbeatFreshTaskIds).length > 0
&& diagnosticsHeartbeatRiskTaskIds(diagnostics).length === 0;
}
function diagnosticsEffectiveLiveness(diagnostics: any): string {
const explicit = String(diagnostics?.effectiveLiveness || "").toLowerCase();
if (explicit) return explicit;
if (diagnosticsHeartbeatRiskTaskIds(diagnostics).length > 0) return "at-risk";
if (splitBrainLiveDiagnostics(diagnostics)) return "live";
const state = String(diagnostics?.state || diagnostics?.health || "unknown").toLowerCase();
return state === "healthy" ? "healthy" : state === "unknown" ? "unknown" : "degraded";
}
function diagnosticsTone(diagnostics: any): string {
const effective = diagnosticsEffectiveLiveness(diagnostics);
if (effective === "live" || effective === "degraded") return "warn";
if (effective === "at-risk") return "failed";
const value = String(diagnostics?.state || diagnostics?.health || "unknown").toLowerCase();
if (value === "healthy") return "ok";
if (value === "split-brain" || value === "stale-active") return "failed";
if (value === "stale-active") return "failed";
if (value === "degraded") return "warn";
return "unknown";
}
@@ -1576,12 +1605,25 @@ function CodeQueueLivenessPanel({ diagnostics, queue, onRaw }: AnyRecord) {
const state = String(diagnostics?.state || diagnostics?.health || "unknown");
const oaPublisher = objectRecord(diagnostics?.oaPublisher);
const reasons = stringArray(diagnostics?.reasons).slice(0, 3);
const tone = diagnosticsTone(state);
const heartbeatRiskTaskIds = diagnosticsHeartbeatRiskTaskIds(diagnostics);
const splitBrainLive = splitBrainLiveDiagnostics(diagnostics);
const effectiveLiveness = diagnosticsEffectiveLiveness(diagnostics);
const recommendedAction = String(diagnostics?.recommendedAction || (heartbeatRiskTaskIds.length > 0 ? "investigate-heartbeat-risk" : splitBrainLive ? "continue-supervision" : effectiveLiveness === "degraded" ? "observe-degraded" : "none"));
const livenessText = String(splitBrainLive
? "执行面 heartbeat 新鲜,任务仍应继续监督。"
: heartbeatRiskTaskIds.length > 0
? "存在 expired/missing/stale heartbeat 风险,请先确认执行面状态。"
: diagnostics?.livenessSummary || (effectiveLiveness === "degraded"
? "Diagnostics are degraded; inspect heartbeat, trace, and OA progress together."
: "Execution diagnostics are healthy."));
const tone = diagnosticsTone(diagnostics);
const stateLabel = splitBrainLive ? "split-brain live" : state;
return h(Panel, {
title: "执行活性",
eyebrow: `${String(diagnostics?.executionStateSource || queue?.executionStateSource || "unknown")} / ${String(diagnostics?.controlPlane || "code-queue")}`,
summary: h("div", { className: "codex-trace-status" },
h("span", { className: `codex-trace-status-chip liveness ${tone}` }, h("b", null, "状态"), state),
h("span", { className: `codex-trace-status-chip liveness ${tone}` }, h("b", null, "状态"), stateLabel),
h("span", { className: `codex-trace-status-chip liveness ${tone}` }, h("b", null, "liveness"), effectiveLiveness),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "DB active"), String(diagnostics?.databaseActiveTaskCount ?? queue?.databaseActiveTaskCount ?? 0)),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "scheduler slots"), String(diagnostics?.schedulerActiveRunSlotCount ?? queue?.activeRunSlotCount ?? 0)),
h("span", { className: "codex-trace-status-chip" }, h("b", null, "heartbeat"), `${stringArray(diagnostics?.heartbeatFreshTaskIds).length} fresh / ${stringArray(diagnostics?.heartbeatExpiredTaskIds).length} expired`),
@@ -1591,10 +1633,12 @@ function CodeQueueLivenessPanel({ diagnostics, queue, onRaw }: AnyRecord) {
className: "codex-liveness-panel",
},
h("div", { className: "codex-liveness-grid", "data-testid": "codex-liveness-diagnostics" },
h(LivenessMetric, { tone, label: "健康状态", value: state, hint: diagnostics?.splitBrain ? "split-brain" : diagnostics?.degraded ? "degraded" : "ready" }),
h(LivenessMetric, { tone, label: "健康状态", value: stateLabel, hint: recommendedAction }),
h(LivenessMetric, { tone, label: "Effective liveness", value: effectiveLiveness, hint: livenessText }),
h(LivenessMetric, { label: "PostgreSQL active", value: String(diagnostics?.databaseActiveTaskCount ?? queue?.databaseActiveTaskCount ?? 0), hint: compactIdList(diagnostics?.databaseActiveTaskIds ?? queue?.databaseActiveTaskIds) }),
h(LivenessMetric, { label: "Scheduler active", value: String(diagnostics?.schedulerActiveRunSlotCount ?? queue?.activeRunSlotCount ?? 0), hint: compactIdList(diagnostics?.schedulerActiveTaskIds ?? queue?.activeTaskIds) }),
h(LivenessMetric, { label: "Fresh heartbeat", value: String(stringArray(diagnostics?.heartbeatFreshTaskIds).length), hint: compactIdList(diagnostics?.heartbeatFreshTaskIds) }),
h(LivenessMetric, { tone: heartbeatRiskTaskIds.length > 0 ? "failed" : splitBrainLive ? "warn" : "", label: "Heartbeat risk", value: String(heartbeatRiskTaskIds.length), hint: heartbeatRiskTaskIds.length > 0 ? compactIdList(heartbeatRiskTaskIds) : splitBrainLive ? "fresh heartbeat: keep supervising" : "--" }),
h(LivenessMetric, { tone: stringArray(diagnostics?.traceGapNotStaleTaskIds).length > 0 ? "warn" : "", label: "Trace gap", value: String(stringArray(diagnostics?.traceGapTaskIds).length), hint: compactIdList(diagnostics?.traceGapNotStaleTaskIds) }),
h(LivenessMetric, { tone: stringArray(diagnostics?.staleRecoveryCandidateTaskIds).length > 0 ? "failed" : "", label: "Stale candidates", value: String(stringArray(diagnostics?.staleRecoveryCandidateTaskIds).length), hint: compactIdList(diagnostics?.staleRecoveryCandidateTaskIds) }),
h(LivenessMetric, { label: "Last scheduler heartbeat", value: fmtRelativeAge(diagnostics?.lastSchedulerHeartbeatAt), hint: String(diagnostics?.lastSchedulerHeartbeatAt || "--") }),
@@ -1602,6 +1646,10 @@ function CodeQueueLivenessPanel({ diagnostics, queue, onRaw }: AnyRecord) {
h(LivenessMetric, { label: "Last trace persist", value: fmtRelativeAge(diagnostics?.lastPersistedTraceAt), hint: String(diagnostics?.lastPersistedTraceAt || "--") }),
h(LivenessMetric, { tone: oaPublisher?.lastError ? "warn" : "", label: "OA publisher", value: `${Number(oaPublisher?.pending || 0)} pending`, hint: oaPublisher?.lastError ? shortText(oaPublisher.lastError, 90) : "ok" }),
),
h("div", { className: `codex-liveness-advisory ${tone}` },
h("b", null, splitBrainLive ? "Observing split" : heartbeatRiskTaskIds.length > 0 ? "Heartbeat risk" : "Liveness note"),
h("span", null, livenessText),
),
reasons.length > 0 ? h("div", { className: "codex-liveness-reasons" }, reasons.map((reason: string) => h("span", { key: reason }, reason))) : null,
);
}