diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 8cddd017..8cfdbcd9 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -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", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 67bde74a..d3098f05 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -11578,8 +11578,10 @@ "title": "AI 受控執行閉環", "refresh": "重新整理", "completion": "完成 {percent}%", + "completionLoading": "完成度讀取中", "detail": "Incident {incident} / op {op} / {catalog}", "states": { + "loading": "讀取正式 readback", "closed": "閉環完成", "open": "閉環中", "degraded": "讀取降級", diff --git a/apps/web/src/components/awooop/autonomous-runtime-receipt-panel.tsx b/apps/web/src/components/awooop/autonomous-runtime-receipt-panel.tsx index 11b53e65..040824c7 100644 --- a/apps/web/src/components/awooop/autonomous-runtime-receipt-panel.tsx +++ b/apps/web/src/components/awooop/autonomous-runtime-receipt-panel.tsx @@ -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} - {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")}

@@ -931,9 +991,9 @@ export function AutonomousRuntimeReceiptPanel({

{card.label}

- {numberValue(writeCount)} + {readbackNumber(writeCount, hasConsumerReadback)} {" / "} - {numberValue(bindingCount)} + {readbackNumber(bindingCount, hasConsumerReadback)}