diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index 3b70cddd..9a3d7bca 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -3852,6 +3852,45 @@
"overrideTitle": "舊規範覆寫",
"hardBlockerTitle": "仍維持硬阻擋",
"hardBlockerDetail": "需 break-glass 或專案級合約,不由一般自動化靜默執行。",
+ "cockpit": {
+ "liveLabel": "LIVE PRODUCTION",
+ "title": "AI Agent Cockpit",
+ "subtitle": "Production readback for the active control layer, closed loop, receipts, and hard boundaries.",
+ "production": "Production",
+ "dbOk": "DB readback OK",
+ "dbReview": "DB readback review",
+ "workItems": "Work items",
+ "consumer": "LOG consumer",
+ "apply": "Apply",
+ "verifier": "Verifier",
+ "km": "KM",
+ "telegram": "Telegram",
+ "receiptDetail": "controlled runtime receipt",
+ "verifierDetail": "post-apply verifier",
+ "kmDetail": "KM / PlayBook writeback",
+ "telegramDetail": "Gateway receipt",
+ "loopTitle": "Runtime loop",
+ "closed": "closed",
+ "review": "review",
+ "missing": "missing",
+ "runtimeWrite": "runtime write",
+ "noRuntimeWrite": "no runtime write",
+ "riskTitle": "Controlled risk lanes",
+ "on": "ON",
+ "off": "OFF",
+ "ownerRequired": "owner review required",
+ "ownerNotRequired": "owner review not required",
+ "criticalBreakGlass": "critical break-glass",
+ "criticalReview": "critical review"
+ },
+ "flow": {
+ "candidate": "Candidate",
+ "checkMode": "Check-mode",
+ "apply": "Controlled apply",
+ "verifier": "Verifier",
+ "learning": "KM learning",
+ "telegram": "Telegram"
+ },
"readback": {
"marker": "Production v2 marker",
"markerDetail": "{task} · {status}",
diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json
index 2de74a28..c5c29f88 100644
--- a/apps/web/messages/zh-TW.json
+++ b/apps/web/messages/zh-TW.json
@@ -3852,6 +3852,45 @@
"overrideTitle": "舊規範覆寫",
"hardBlockerTitle": "仍維持硬阻擋",
"hardBlockerDetail": "需 break-glass 或專案級合約,不由一般自動化靜默執行。",
+ "cockpit": {
+ "liveLabel": "LIVE PRODUCTION",
+ "title": "AI Agent Cockpit",
+ "subtitle": "用 production readback 呈現控制層、閉環、收據與硬邊界。",
+ "production": "Production",
+ "dbOk": "DB readback OK",
+ "dbReview": "DB readback review",
+ "workItems": "Work items",
+ "consumer": "LOG consumer",
+ "apply": "Apply",
+ "verifier": "Verifier",
+ "km": "KM",
+ "telegram": "Telegram",
+ "receiptDetail": "controlled runtime receipt",
+ "verifierDetail": "post-apply verifier",
+ "kmDetail": "KM / PlayBook writeback",
+ "telegramDetail": "Gateway receipt",
+ "loopTitle": "Runtime loop",
+ "closed": "closed",
+ "review": "review",
+ "missing": "missing",
+ "runtimeWrite": "runtime write",
+ "noRuntimeWrite": "no runtime write",
+ "riskTitle": "Controlled risk lanes",
+ "on": "ON",
+ "off": "OFF",
+ "ownerRequired": "owner review required",
+ "ownerNotRequired": "owner review not required",
+ "criticalBreakGlass": "critical break-glass",
+ "criticalReview": "critical review"
+ },
+ "flow": {
+ "candidate": "Candidate",
+ "checkMode": "Check-mode",
+ "apply": "Controlled apply",
+ "verifier": "Verifier",
+ "learning": "KM learning",
+ "telegram": "Telegram"
+ },
"readback": {
"marker": "Production v2 marker",
"markerDetail": "{task} · {status}",
diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx
index ec5648f3..ac99bd6c 100644
--- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx
+++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx
@@ -853,6 +853,237 @@ function GateMatrixRow({
)
}
+function AutonomyCockpitMetric({
+ label,
+ value,
+ detail,
+ tone = 'neutral',
+ icon,
+}: {
+ label: string
+ value: number | string
+ detail: string
+ tone?: 'ok' | 'warn' | 'danger' | 'neutral'
+ icon: ReactNode
+}) {
+ const color = toneColor(tone)
+ return (
+
+
+ {icon}
+
+
+ {label}
+
+ {value}
+
+
+ {redactPublicText(detail)}
+
+
+
+ )
+}
+
+function AutonomyFlowStep({
+ label,
+ status,
+ detail,
+ tone = 'neutral',
+ icon,
+}: {
+ label: string
+ status: string
+ detail: string
+ tone?: 'ok' | 'warn' | 'danger' | 'neutral'
+ icon: ReactNode
+}) {
+ const color = toneColor(tone)
+ return (
+
+
+
+ {icon}
+
+
+ {redactPublicText(status)}
+
+
+
+ {redactPublicText(label)}
+
+
+
+ {redactPublicText(detail)}
+
+
+ )
+}
+
+function autonomyStageTone(stage: { present: boolean; status: string } | undefined, loopClosed: boolean): 'ok' | 'warn' | 'danger' | 'neutral' {
+ if (!stage?.present) return 'danger'
+ if (['success', 'sent', 'REVIEW', 'inferred_from_check_mode'].includes(stage.status)) return 'ok'
+ if (stage.status === 'failed' && loopClosed) return 'warn'
+ if (stage.status === 'failed') return 'danger'
+ return 'neutral'
+}
+
+function AutonomyWorkspaceScene({
+ stations,
+ completion,
+ status,
+}: {
+ stations: Array<{
+ key: string
+ label: string
+ value: number | string
+ tone: 'ok' | 'warn' | 'danger' | 'neutral'
+ icon: ReactNode
+ }>
+ completion: number
+ status: string
+}) {
+ const safeCompletion = Math.max(0, Math.min(100, completion))
+ return (
+
+
+
+
+
+
+
+
+
+ {safeCompletion}%
+
+
+ {redactPublicText(status)}
+
+
+
+
+ {stations.map((station, index) => {
+ const color = toneColor(station.tone)
+ return (
+
+
+
+ {station.icon}
+
+
+ {station.value}
+
+
+
{station.label}
+
+ )
+ })}
+
+
+ )
+}
+
function AutonomousRuntimeControlReadbackGrid({
control,
t,
@@ -5782,6 +6013,99 @@ export function AutomationInventoryTab() {
const currentAutonomyHardBlockers = autonomousRuntimeControl?.hard_blockers ?? []
const currentAutonomyPolicy = autonomousRuntimeControl?.current_policy
const currentAutonomySwitches = autonomousRuntimeControl?.runtime_switches
+ const currentAutonomyRuntimeReadback = autonomousRuntimeControl?.runtime_receipt_readback
+ const currentAutonomyLoopLedger = currentAutonomyRuntimeReadback?.autonomous_execution_loop_ledger
+ const currentAutonomyLoopClosed = currentAutonomyLoopLedger?.closed === true
+ const currentAutonomyStageById = new Map((currentAutonomyLoopLedger?.stages ?? []).map(stage => [stage.stage_id, stage]))
+ const currentAutonomyFlowStages = [
+ {
+ key: 'candidate',
+ label: t('globalControl.currentAutonomy.flow.candidate'),
+ stage: currentAutonomyStageById.get('candidate'),
+ icon: ,
+ },
+ {
+ key: 'checkMode',
+ label: t('globalControl.currentAutonomy.flow.checkMode'),
+ stage: currentAutonomyStageById.get('check_mode'),
+ icon: ,
+ },
+ {
+ key: 'apply',
+ label: t('globalControl.currentAutonomy.flow.apply'),
+ stage: currentAutonomyStageById.get('controlled_apply'),
+ icon: ,
+ },
+ {
+ key: 'verifier',
+ label: t('globalControl.currentAutonomy.flow.verifier'),
+ stage: currentAutonomyStageById.get('post_apply_verifier'),
+ icon: ,
+ },
+ {
+ key: 'learning',
+ label: t('globalControl.currentAutonomy.flow.learning'),
+ stage: currentAutonomyStageById.get('km_playbook_writeback'),
+ icon: ,
+ },
+ {
+ key: 'telegram',
+ label: t('globalControl.currentAutonomy.flow.telegram'),
+ stage: currentAutonomyStageById.get('telegram_receipt'),
+ icon: ,
+ },
+ ]
+ const currentAutonomyReceiptStats = [
+ {
+ key: 'apply',
+ label: t('globalControl.currentAutonomy.cockpit.apply'),
+ value: autonomousRuntimeControl?.rollups.live_ansible_apply_executed_count ?? 0,
+ detail: t('globalControl.currentAutonomy.cockpit.receiptDetail'),
+ tone: (autonomousRuntimeControl?.rollups.live_ansible_apply_executed_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
+ icon: ,
+ },
+ {
+ key: 'verifier',
+ label: t('globalControl.currentAutonomy.cockpit.verifier'),
+ value: autonomousRuntimeControl?.rollups.live_post_apply_verifier_count ?? 0,
+ detail: t('globalControl.currentAutonomy.cockpit.verifierDetail'),
+ tone: (autonomousRuntimeControl?.rollups.live_post_apply_verifier_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
+ icon: ,
+ },
+ {
+ key: 'km',
+ label: t('globalControl.currentAutonomy.cockpit.km'),
+ value: autonomousRuntimeControl?.rollups.live_km_writeback_count ?? 0,
+ detail: t('globalControl.currentAutonomy.cockpit.kmDetail'),
+ tone: (autonomousRuntimeControl?.rollups.live_km_writeback_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
+ icon: ,
+ },
+ {
+ key: 'telegram',
+ label: t('globalControl.currentAutonomy.cockpit.telegram'),
+ value: autonomousRuntimeControl?.rollups.live_telegram_receipt_count ?? 0,
+ detail: t('globalControl.currentAutonomy.cockpit.telegramDetail'),
+ tone: (autonomousRuntimeControl?.rollups.live_telegram_receipt_count ?? 0) > 0 ? 'ok' as const : 'warn' as const,
+ icon: ,
+ },
+ ]
+ const currentAutonomyRiskRows = [
+ {
+ key: 'low',
+ label: t('globalControl.currentAutonomy.policy.low'),
+ enabled: currentAutonomyPolicy?.low_risk_controlled_apply_allowed === true,
+ },
+ {
+ key: 'medium',
+ label: t('globalControl.currentAutonomy.policy.medium'),
+ enabled: currentAutonomyPolicy?.medium_risk_controlled_apply_allowed === true,
+ },
+ {
+ key: 'high',
+ label: t('globalControl.currentAutonomy.policy.high'),
+ enabled: currentAutonomyPolicy?.high_risk_controlled_apply_allowed === true,
+ },
+ ]
const globalControlRunwayRows: Array<{
key: string
label: string
@@ -6592,9 +6916,19 @@ export function AutomationInventoryTab() {
{autonomousRuntimeControl ? (
-
-
-
+
+
+
-
- {t('globalControl.currentAutonomy.title')}
+ {t('globalControl.currentAutonomy.cockpit.liveLabel')}
+
+ {t('globalControl.currentAutonomy.cockpit.title')}
-
- {autonomousRuntimeControl.program_status.status_note}
+
+ {t('globalControl.currentAutonomy.cockpit.subtitle')}
-
-
+
-
-
} />
-
} />
-
} />
-
} />
-
} />
-
} />
+
+
+
+ {t('globalControl.currentAutonomy.metrics.completion')}
+
+
+
+
+ {autonomousRuntimeControl.program_status.implementation_completion_percent}%
+
+
+
+
+ {autonomousRuntimeControl.program_status.deploy_readback_marker}
+
+
+
+
+
+
+
+
+
+
+
+ {currentAutonomyReceiptStats.map(stat => (
+
+ ))}
+
-
-
+
+
+ {t('globalControl.currentAutonomy.cockpit.loopTitle')}
+
+
+
+ {currentAutonomyFlowStages.map(row => (
+
+ ))}
+
+
+
+
+
- {t('globalControl.currentAutonomy.policyTitle')}
-
+ {t('globalControl.currentAutonomy.cockpit.riskTitle')}
+
-
-
-
-
-
-
- {currentAutonomyCadences.map(cadence => (
-
-
-
- {cadence.display_name}
-
-
-
-
- {cadence.schedule}
-
-
- {cadence.worker}
+
+ {currentAutonomyRiskRows.map(row => (
+
+ {row.label}
+
+ {row.enabled ? t('globalControl.currentAutonomy.cockpit.on') : t('globalControl.currentAutonomy.cockpit.off')}
))}
+
+
+
+
+
-
- {t('globalControl.currentAutonomy.runtimeTitle')}
-
-
-
-
-
-
-
-
-
{t('globalControl.currentAutonomy.executorTitle')}
- {currentAutonomyExecutorReceipts.slice(0, 5).map(receipt => (
-
+
+ {t('globalControl.currentAutonomy.runtimeTitle')}
+
+
+
-
- {t('globalControl.currentAutonomy.overrideTitle')}
- {currentAutonomyOverrides.slice(0, 4).map(row => (
- }
/>
- ))}
-
-
- {t('globalControl.currentAutonomy.hardBlockerTitle')}
- {currentAutonomyHardBlockers.slice(0, 5).map(blocker => (
- cadence.cadence).join(' / ') || '--'}
+ tone={currentAutonomyCadences.length > 0 ? 'ok' : 'warn'}
+ icon={}
/>
- ))}
+
+
+
+
+
@@ -20187,7 +20526,117 @@ export function AutomationInventoryTab() {
white-space: normal !important;
}
+ .automation-inventory-autonomy-workspace-rail {
+ position: absolute;
+ inset: 28px;
+ border: 0.5px solid rgba(21, 128, 61, 0.18);
+ border-radius: 8px;
+ background:
+ linear-gradient(90deg, rgba(21, 128, 61, 0.08) 1px, transparent 1px),
+ linear-gradient(0deg, rgba(21, 128, 61, 0.06) 1px, transparent 1px);
+ background-size: 44px 44px;
+ opacity: 0.88;
+ }
+
+ .automation-inventory-autonomy-workspace-packet {
+ position: absolute;
+ z-index: 1;
+ width: 10px;
+ height: 10px;
+ border-radius: 3px;
+ background: #15803d;
+ box-shadow: 0 0 0 4px rgba(21, 128, 61, 0.12), 0 8px 18px rgba(21, 128, 61, 0.22);
+ }
+
+ .automation-inventory-autonomy-workspace-packet-a {
+ top: 48%;
+ left: 10%;
+ animation: autonomy-packet-x 4.8s linear infinite;
+ }
+
+ .automation-inventory-autonomy-workspace-packet-b {
+ top: 22%;
+ left: 48%;
+ animation: autonomy-packet-y 5.4s linear infinite;
+ animation-delay: 0.8s;
+ }
+
+ .automation-inventory-autonomy-workspace-packet-c {
+ top: 72%;
+ left: 16%;
+ animation: autonomy-packet-x 6.2s linear infinite;
+ animation-delay: 1.6s;
+ }
+
+ .automation-inventory-autonomy-workspace-station {
+ animation: autonomy-station-breathe 3.8s ease-in-out infinite;
+ }
+
+ .automation-inventory-autonomy-workspace-station-1 {
+ animation-delay: 0.25s;
+ }
+
+ .automation-inventory-autonomy-workspace-station-2 {
+ animation-delay: 0.5s;
+ }
+
+ .automation-inventory-autonomy-workspace-station-3 {
+ animation-delay: 0.75s;
+ }
+
+ @keyframes autonomy-packet-x {
+ 0% {
+ left: 10%;
+ opacity: 0;
+ }
+ 12% {
+ opacity: 1;
+ }
+ 88% {
+ opacity: 1;
+ }
+ 100% {
+ left: 88%;
+ opacity: 0;
+ }
+ }
+
+ @keyframes autonomy-packet-y {
+ 0% {
+ top: 18%;
+ opacity: 0;
+ }
+ 12% {
+ opacity: 1;
+ }
+ 88% {
+ opacity: 1;
+ }
+ 100% {
+ top: 78%;
+ opacity: 0;
+ }
+ }
+
+ @keyframes autonomy-station-breathe {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-2px);
+ }
+ }
+
@media (max-width: 900px) {
+ .automation-inventory-autonomy-cockpit-header,
+ .automation-inventory-autonomy-cockpit-hero-grid,
+ .automation-inventory-autonomy-cockpit-score-grid,
+ .automation-inventory-autonomy-cockpit-metric-grid,
+ .automation-inventory-autonomy-cockpit-flow-grid,
+ .automation-inventory-autonomy-cockpit-control-grid,
+ .automation-inventory-autonomy-cockpit-risk-grid,
+ .automation-inventory-autonomy-cockpit-runtime-grid,
+ .automation-inventory-autonomy-workspace-grid,
.automation-inventory-visual-grid,
.automation-inventory-stage-grid,
.automation-inventory-visual-factor-grid,
@@ -20269,6 +20718,25 @@ export function AutomationInventoryTab() {
overflow-x: hidden;
}
+ .automation-inventory-autonomy-workspace-scene {
+ min-height: 430px !important;
+ }
+
+ .automation-inventory-autonomy-workspace-console {
+ position: relative !important;
+ inset: auto !important;
+ transform: none !important;
+ width: 100% !important;
+ height: auto !important;
+ min-height: 104px;
+ margin-bottom: 10px;
+ }
+
+ .automation-inventory-autonomy-workspace-rail,
+ .automation-inventory-autonomy-workspace-packet {
+ display: none;
+ }
+
.automation-inventory-tab-root button,
.automation-inventory-tab-root [role='button'],
.automation-inventory-tab-root span,
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts
index 53e39660..76896665 100644
--- a/apps/web/src/lib/api-client.ts
+++ b/apps/web/src/lib/api-client.ts
@@ -2213,6 +2213,11 @@ export interface AiAgentAutonomousRuntimeControlSnapshot {
live_km_writeback_count?: number
live_post_apply_verifier_count?: number
live_telegram_receipt_count?: number
+ live_log_controlled_writeback_consumer_apply_receipt_count?: number
+ live_log_controlled_writeback_consumer_dispatch_ledger_count?: number
+ live_log_controlled_writeback_runtime_target_write_count?: number
+ live_work_item_completed_count?: number
+ live_work_item_blocked_count?: number
mcp_sensor_count?: number
rag_context_query_count?: number
playbook_decision_class_count?: number