fix(web): avoid ai loop zero-state readback flash
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-02 11:01:12 +08:00
parent 6e881bd835
commit 4558ac9563
3 changed files with 174 additions and 71 deletions

View File

@@ -11578,8 +11578,10 @@
"title": "AI Controlled Execution Loop",
"refresh": "Refresh",
"completion": "{percent}% complete",
"completionLoading": "Completion reading",
"detail": "Incident {incident} / op {op} / {catalog}",
"states": {
"loading": "Reading production readback",
"closed": "Loop closed",
"open": "Loop open",
"degraded": "Read degraded",

View File

@@ -11578,8 +11578,10 @@
"title": "AI 受控執行閉環",
"refresh": "重新整理",
"completion": "完成 {percent}%",
"completionLoading": "完成度讀取中",
"detail": "Incident {incident} / op {op} / {catalog}",
"states": {
"loading": "讀取正式 readback",
"closed": "閉環完成",
"open": "閉環中",
"degraded": "讀取降級",

View File

@@ -275,6 +275,16 @@ function toNumber(value: unknown): number {
return Number.isFinite(numeric) ? numeric : 0;
}
function readbackNumber(value: unknown, ready: boolean): string {
if (!ready) return "--";
return numberValue(value);
}
function readbackRatio(done: unknown, total: unknown, ready: boolean): string {
if (!ready) return "-- / --";
return `${numberValue(done)} / ${numberValue(total)}`;
}
function shortRef(value?: string | null): string {
if (!value) return "--";
return value.length > 12 ? `${value.slice(0, 8)}...` : value;
@@ -397,6 +407,9 @@ export function AutonomousRuntimeReceiptPanel({
return () => window.clearInterval(timer);
}, [refresh]);
const hasRuntimeReadback = payload !== null;
const hasPriorityReadback = priorityPayload !== null;
const hasConsumerReadback = consumerPayload !== null;
const readback = payload?.runtime_receipt_readback;
const ledger = readback?.autonomous_execution_loop_ledger;
const traceLedger = readback?.trace_ledger;
@@ -428,8 +441,11 @@ export function AutonomousRuntimeReceiptPanel({
?? ledger?.missing_stage_ids
?? latestFlow?.missing
?? [];
const tone: Tone = error || !dbOk ? "warn" : closed ? "ok" : "neutral";
const stateLabel = closed
const initialRuntimeReadInFlight = loading && !hasRuntimeReadback && !error;
const tone: Tone = initialRuntimeReadInFlight ? "neutral" : error || !dbOk ? "warn" : closed ? "ok" : "neutral";
const stateLabel = initialRuntimeReadInFlight
? t("states.loading")
: closed
? t("states.closed")
: error
? t("states.unavailable")
@@ -633,102 +649,136 @@ export function AutonomousRuntimeReceiptPanel({
const deployedSourceShortSha = prioritySummary?.latest_successful_deployed_source_short_sha
?? (deployedSourceSha ? deployedSourceSha.slice(0, 10) : null);
const deployReadbackResolved = prioritySummary?.ai_loop_current_blocker_deploy_marker_resolved_by_production_readback === true;
const deployCardTone: Tone = !hasPriorityReadback ? "neutral" : deployReadbackResolved ? "ok" : "warn";
const runtimeCardTone: Tone = !hasRuntimeReadback ? "neutral" : dbOk ? "ok" : "warn";
const workCardTone: Tone = !hasRuntimeReadback ? "neutral" : workTotal > 0 && workCompleted === workTotal ? "ok" : "warn";
const sourceCardTone: Tone = !hasRuntimeReadback
? "neutral"
: toNumber(rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count) > 0
? "ok"
: "warn";
const eventCardTone: Tone = !hasRuntimeReadback
? "neutral"
: toNumber(rollups.live_log_classified_event_total ?? logRollups.classified_event_total) > 0
? "ok"
: "neutral";
const consumerCardTone: Tone = !hasConsumerReadback ? "neutral" : consumerReady ? "ok" : "warn";
const proofCards = [
{
key: "deploy",
label: t("proof.deploy"),
value: deployedSourceShortSha ?? "--",
detail: t("proof.deployDetail", {
status: mainlineState?.current_main_cd_run_status ?? priorityPayload?.status ?? "--",
status: hasPriorityReadback
? mainlineState?.current_main_cd_run_status ?? priorityPayload?.status ?? "--"
: t("states.loading"),
}),
icon: Rocket,
tone: deployReadbackResolved ? "ok" as Tone : "warn" as Tone,
tone: deployCardTone,
},
{
key: "runtime",
label: t("proof.runtime"),
value: dbOk ? t("proof.ok") : t("proof.degraded"),
value: !hasRuntimeReadback ? t("states.loading") : dbOk ? t("proof.ok") : t("proof.degraded"),
detail: t("proof.runtimeDetail", {
marker: shortRef(payload?.program_status?.deploy_readback_marker),
}),
icon: Database,
tone: dbOk ? "ok" as Tone : "warn" as Tone,
tone: runtimeCardTone,
},
{
key: "work",
label: t("proof.workItems"),
value: `${numberValue(workCompleted)}/${numberValue(workTotal)}`,
value: readbackRatio(workCompleted, workTotal, hasRuntimeReadback),
detail: t("proof.workItemsDetail", {
percent: numberValue(workCompletionPercent),
percent: readbackNumber(workCompletionPercent, hasRuntimeReadback),
}),
icon: ListChecks,
tone: workTotal > 0 && workCompleted === workTotal ? "ok" as Tone : "warn" as Tone,
tone: workCardTone,
},
{
key: "sources",
label: t("proof.sources"),
value: `${numberValue(rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count)}/${numberValue(rollups.live_log_source_family_count ?? logRollups.source_family_count)}`,
value: readbackRatio(
rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count,
rollups.live_log_source_family_count ?? logRollups.source_family_count,
hasRuntimeReadback
),
detail: t("proof.sourcesDetail"),
icon: Bot,
tone: toNumber(rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count) > 0 ? "ok" as Tone : "warn" as Tone,
tone: sourceCardTone,
},
{
key: "events",
label: t("proof.events"),
value: numberValue(rollups.live_log_classified_event_total ?? logRollups.classified_event_total),
value: readbackNumber(
rollups.live_log_classified_event_total ?? logRollups.classified_event_total,
hasRuntimeReadback
),
detail: t("proof.eventsDetail", {
recent: numberValue(rollups.live_log_recent_classified_event_total ?? logRollups.recent_classified_event_total),
recent: readbackNumber(
rollups.live_log_recent_classified_event_total ?? logRollups.recent_classified_event_total,
hasRuntimeReadback
),
}),
icon: Activity,
tone: toNumber(rollups.live_log_classified_event_total ?? logRollups.classified_event_total) > 0 ? "ok" as Tone : "neutral" as Tone,
tone: eventCardTone,
},
{
key: "consumer",
label: t("proof.consumer"),
value: `${numberValue(
value: readbackRatio(
rollups.live_log_controlled_writeback_consumer_apply_receipt_count
?? consumerRollups.consumer_apply_receipt_row_count
)}/${numberValue(
?? consumerRollups.consumer_apply_receipt_row_count,
rollups.live_log_controlled_writeback_consumer_dispatch_ledger_count
?? consumerRollups.dispatch_ledger_row_count
)}`,
?? consumerRollups.dispatch_ledger_row_count,
hasConsumerReadback || hasRuntimeReadback
),
detail: t("proof.consumerDetail", {
targets: numberValue(consumerRollups.ready_target_count),
targets: readbackNumber(consumerRollups.ready_target_count, hasConsumerReadback),
}),
icon: Database,
tone: consumerReady ? "ok" as Tone : "warn" as Tone,
tone: consumerCardTone,
},
];
const writebackCards = [
{
key: "dispatch",
label: t("writeback.dispatch"),
value: numberValue(
value: readbackNumber(
rollups.live_log_controlled_writeback_consumer_dispatch_ledger_count
?? consumerRollups.dispatch_ledger_row_count
?? consumerRollups.dispatch_ledger_row_count,
hasConsumerReadback || hasRuntimeReadback
),
detail: t("writeback.dispatchDetail"),
icon: Send,
tone: consumerReady ? "ok" as Tone : "warn" as Tone,
tone: consumerCardTone,
},
{
key: "apply",
label: t("writeback.apply"),
value: numberValue(
value: readbackNumber(
rollups.live_log_controlled_writeback_consumer_apply_receipt_count
?? consumerRollups.consumer_apply_receipt_row_count
?? consumerRollups.consumer_apply_receipt_row_count,
hasConsumerReadback || hasRuntimeReadback
),
detail: t("writeback.applyDetail"),
icon: CheckCircle2,
tone: runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
tone: !hasConsumerReadback && !hasRuntimeReadback ? "neutral" as Tone : runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
},
{
key: "bindings",
label: t("writeback.bindings"),
value: `${numberValue(consumerRollups.ready_consumer_binding_count)}/${numberValue(consumerRollups.consumer_binding_count)}`,
value: readbackRatio(
consumerRollups.ready_consumer_binding_count,
consumerRollups.consumer_binding_count,
hasConsumerReadback
),
detail: t("writeback.bindingsDetail"),
icon: Bot,
tone: toNumber(consumerRollups.ready_consumer_binding_count) === toNumber(consumerRollups.consumer_binding_count)
tone: !hasConsumerReadback
? "neutral" as Tone
: toNumber(consumerRollups.ready_consumer_binding_count) === toNumber(consumerRollups.consumer_binding_count)
&& toNumber(consumerRollups.consumer_binding_count) > 0
? "ok" as Tone
: "warn" as Tone,
@@ -736,26 +786,34 @@ export function AutonomousRuntimeReceiptPanel({
{
key: "targets",
label: t("writeback.targets"),
value: numberValue(consumerRollups.ready_target_count),
value: readbackNumber(consumerRollups.ready_target_count, hasConsumerReadback),
detail: t("writeback.targetsDetail"),
icon: ListChecks,
tone: toNumber(consumerRollups.ready_target_count) >= 6 ? "ok" as Tone : "warn" as Tone,
tone: !hasConsumerReadback
? "neutral" as Tone
: toNumber(consumerRollups.ready_target_count) >= 6 ? "ok" as Tone : "warn" as Tone,
},
{
key: "context",
label: t("writeback.contextWrites"),
value: numberValue(consumerRollups.target_context_receipt_write_count),
value: readbackNumber(consumerRollups.target_context_receipt_write_count, hasConsumerReadback),
detail: t("writeback.contextWritesDetail"),
icon: BookOpenCheck,
tone: runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
tone: !hasConsumerReadback
? "neutral" as Tone
: runtimeTargetWritePerformed ? "ok" as Tone : "neutral" as Tone,
},
{
key: "blockers",
label: t("writeback.blockers"),
value: numberValue(consumerBlockers.length),
detail: consumerBlockers.length > 0 ? consumerBlockers[0] : t("writeback.noBlockers"),
value: readbackNumber(consumerBlockers.length, hasConsumerReadback),
detail: !hasConsumerReadback
? t("states.loading")
: consumerBlockers.length > 0 ? consumerBlockers[0] : t("writeback.noBlockers"),
icon: TriangleAlert,
tone: consumerBlockers.length > 0 ? "warn" as Tone : "ok" as Tone,
tone: !hasConsumerReadback
? "neutral" as Tone
: consumerBlockers.length > 0 ? "warn" as Tone : "ok" as Tone,
},
];
const targetWritebackCards = [
@@ -835,9 +893,11 @@ export function AutonomousRuntimeReceiptPanel({
{stateLabel}
</span>
<span className="font-mono text-xs text-[#77736a]">
{t("completion", {
percent: numberValue(payload?.program_status?.implementation_completion_percent ?? 0),
})}
{hasRuntimeReadback
? t("completion", {
percent: numberValue(payload?.program_status?.implementation_completion_percent ?? 0),
})
: t("completionLoading")}
</span>
</div>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
@@ -931,9 +991,9 @@ export function AutonomousRuntimeReceiptPanel({
<div>
<p className="text-xs font-semibold text-[#77736a]">{card.label}</p>
<p className="mt-2 font-mono text-lg font-semibold text-[#141413]">
{numberValue(writeCount)}
{readbackNumber(writeCount, hasConsumerReadback)}
{" / "}
{numberValue(bindingCount)}
{readbackNumber(bindingCount, hasConsumerReadback)}
</p>
</div>
<Icon className="h-4 w-4 text-[#87867f]" aria-hidden="true" />
@@ -951,17 +1011,21 @@ export function AutonomousRuntimeReceiptPanel({
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("metrics.loop")}</p>
<div className="mt-2 flex items-center gap-2">
{closed ? (
{!hasRuntimeReadback ? (
<RefreshCw className="h-4 w-4 animate-spin text-[#87867f]" aria-hidden="true" />
) : closed ? (
<CheckCircle2 className="h-4 w-4 text-[#17602a]" aria-hidden="true" />
) : (
<TriangleAlert className="h-4 w-4 text-[#8a5a08]" aria-hidden="true" />
)}
<span className="font-mono text-lg font-semibold text-[#141413]">
{closed ? "1" : "0"}
{!hasRuntimeReadback ? "--" : closed ? "1" : "0"}
</span>
</div>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{missingStages.length > 0
{!hasRuntimeReadback
? t("states.loading")
: missingStages.length > 0
? t("missing", { count: missingStages.length })
: t("closedDetail")}
</p>
@@ -974,13 +1038,15 @@ export function AutonomousRuntimeReceiptPanel({
<div>
<p className="text-xs font-semibold text-[#77736a]">{metric.label}</p>
<div className="mt-2 font-mono text-lg font-semibold text-[#141413]">
{numberValue(metric.value)}
{readbackNumber(metric.value, hasRuntimeReadback)}
</div>
</div>
<Icon className="h-4 w-4 text-[#87867f]" aria-hidden="true" />
</div>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{"caption" in metric && metric.caption
{!hasRuntimeReadback
? t("states.loading")
: "caption" in metric && metric.caption
? metric.caption
: t("recent", { count: numberValue(metric.recent) })}
</p>
@@ -993,33 +1059,43 @@ export function AutonomousRuntimeReceiptPanel({
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.sources")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count)}
{readbackNumber(
rollups.live_log_active_source_family_count ?? logRollups.active_source_family_count,
hasRuntimeReadback
)}
{" / "}
{numberValue(rollups.live_log_source_family_count ?? logRollups.source_family_count)}
{readbackNumber(
rollups.live_log_source_family_count ?? logRollups.source_family_count,
hasRuntimeReadback
)}
</p>
</div>
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.products")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(
{readbackNumber(
rollups.live_multi_product_taxonomy_contract_ready_count
?? multiProductRollups.contract_ready_product_scope_count
?? multiProductRollups.contract_ready_product_scope_count,
hasRuntimeReadback
)}
{" / "}
{numberValue(
{readbackNumber(
rollups.live_multi_product_taxonomy_product_scope_count
?? multiProductRollups.product_scope_count
?? multiProductRollups.product_scope_count,
hasRuntimeReadback
)}
</p>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{t("taxonomy.productsDetail", {
active: numberValue(
active: readbackNumber(
rollups.live_multi_product_taxonomy_runtime_adapter_active_count
?? multiProductRollups.runtime_adapter_active_count
?? multiProductRollups.runtime_adapter_active_count,
hasRuntimeReadback
),
missing: numberValue(
missing: readbackNumber(
rollups.live_multi_product_taxonomy_missing_dimension_count
?? multiProductRollups.missing_required_dimension_count
?? multiProductRollups.missing_required_dimension_count,
hasRuntimeReadback
),
})}
</p>
@@ -1027,33 +1103,54 @@ export function AutonomousRuntimeReceiptPanel({
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.labels")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(rollups.live_log_label_dimension_count ?? logRollups.label_dimension_count)}
{readbackNumber(
rollups.live_log_label_dimension_count ?? logRollups.label_dimension_count,
hasRuntimeReadback
)}
</p>
</div>
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.events")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(rollups.live_log_classified_event_total ?? logRollups.classified_event_total)}
{readbackNumber(
rollups.live_log_classified_event_total ?? logRollups.classified_event_total,
hasRuntimeReadback
)}
</p>
</div>
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.learning")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(logRollups.learning_source_family_count)}
{readbackNumber(logRollups.learning_source_family_count, hasRuntimeReadback)}
</p>
</div>
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.workItems")}</p>
<p className="mt-1 text-sm font-semibold text-[#141413]">
{numberValue(rollups.live_work_item_completed_count ?? workItemRollups.completed_count)}
{readbackNumber(
rollups.live_work_item_completed_count ?? workItemRollups.completed_count,
hasRuntimeReadback
)}
{" / "}
{numberValue(rollups.live_work_item_count ?? workItemRollups.work_item_count)}
{readbackNumber(
rollups.live_work_item_count ?? workItemRollups.work_item_count,
hasRuntimeReadback
)}
</p>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{t("taxonomy.workItemsDetail", {
active: numberValue(rollups.live_work_item_in_progress_count ?? workItemRollups.in_progress_count),
pending: numberValue(rollups.live_work_item_pending_count ?? workItemRollups.pending_count),
blocked: numberValue(rollups.live_work_item_blocked_count ?? workItemRollups.blocked_count),
active: readbackNumber(
rollups.live_work_item_in_progress_count ?? workItemRollups.in_progress_count,
hasRuntimeReadback
),
pending: readbackNumber(
rollups.live_work_item_pending_count ?? workItemRollups.pending_count,
hasRuntimeReadback
),
blocked: readbackNumber(
rollups.live_work_item_blocked_count ?? workItemRollups.blocked_count,
hasRuntimeReadback
),
})}
</p>
</div>
@@ -1098,15 +1195,17 @@ export function AutonomousRuntimeReceiptPanel({
<div className="min-w-[180px]">
<div className="flex items-center justify-between gap-3 text-xs font-semibold text-[#5f5b52]">
<span>{t("workBoard.completedOfTotal", {
completed: numberValue(workCompleted),
total: numberValue(workTotal),
completed: readbackNumber(workCompleted, hasRuntimeReadback),
total: readbackNumber(workTotal, hasRuntimeReadback),
})}</span>
<span className="font-mono text-[#141413]">{workCompletionPercent}%</span>
<span className="font-mono text-[#141413]">
{hasRuntimeReadback ? `${workCompletionPercent}%` : "--"}
</span>
</div>
<div className="mt-2 h-2 border border-[#d8d3c7] bg-[#faf9f3]">
<div
className="h-full bg-[#17602a]"
style={{ width: `${workCompletionPercent}%` }}
style={{ width: hasRuntimeReadback ? `${workCompletionPercent}%` : "0%" }}
/>
</div>
</div>
@@ -1130,7 +1229,7 @@ export function AutonomousRuntimeReceiptPanel({
>
<Icon className="h-3.5 w-3.5" aria-hidden="true" />
<span>{t(`workBoard.filters.${filter.key}` as never)}</span>
<span className="font-mono">{numberValue(filter.count)}</span>
<span className="font-mono">{readbackNumber(filter.count, hasRuntimeReadback)}</span>
</button>
);
})}
@@ -1139,7 +1238,7 @@ export function AutonomousRuntimeReceiptPanel({
<div className="bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("workBoard.sourceCoverage")}</p>
<p className="mt-2 font-mono text-lg font-semibold text-[#141413]">
{numberValue(sourceFamilyTotal)}
{readbackNumber(sourceFamilyTotal, hasRuntimeReadback)}
</p>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{t("workBoard.sourceCoverageDetail")}
@@ -1173,7 +1272,7 @@ export function AutonomousRuntimeReceiptPanel({
</div>
)) : (
<div className="bg-white px-4 py-6 text-sm font-semibold text-[#5f5b52]">
{t("workBoard.empty")}
{hasRuntimeReadback ? t("workBoard.empty") : t("states.loading")}
</div>
)}
</div>