feat(governance): 新增 Agent 日週月報風險自動化審查
All checks were successful
CD Pipeline / tests (push) Successful in 1m28s
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / build-and-deploy (push) Successful in 3m59s
CD Pipeline / post-deploy-checks (push) Successful in 1m30s

This commit is contained in:
Your Name
2026-06-12 10:46:56 +08:00
parent 867d3e1472
commit a2bcf03124
19 changed files with 1541 additions and 43 deletions

View File

@@ -45,6 +45,7 @@ import {
type AiAgentPostWriteVerifierPackageSnapshot,
type AiAgentProactiveOperationsContractSnapshot,
type AiAgentRedisDryRunGateSnapshot,
type AiAgentReportAutomationReviewSnapshot,
type AiAgentReportTruthActionabilityReviewSnapshot,
type AiAgentRuntimeVerifierEvidenceReviewSnapshot,
type AiAgentRuntimeWriteGateReviewSnapshot,
@@ -336,6 +337,7 @@ export function AutomationInventoryTab() {
const [runtimeWriteGateReview, setRuntimeWriteGateReview] = useState<AiAgentRuntimeWriteGateReviewSnapshot | null>(null)
const [postWriteVerifierPackage, setPostWriteVerifierPackage] = useState<AiAgentPostWriteVerifierPackageSnapshot | null>(null)
const [runtimeVerifierEvidenceReview, setRuntimeVerifierEvidenceReview] = useState<AiAgentRuntimeVerifierEvidenceReviewSnapshot | null>(null)
const [reportAutomationReview, setReportAutomationReview] = useState<AiAgentReportAutomationReviewSnapshot | null>(null)
const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState<AiAgentReportTruthActionabilityReviewSnapshot | null>(null)
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
@@ -368,6 +370,7 @@ export function AutomationInventoryTab() {
apiClient.getAiAgentRuntimeWriteGateReview(),
apiClient.getAiAgentPostWriteVerifierPackage(),
apiClient.getAiAgentRuntimeVerifierEvidenceReview(),
apiClient.getAiAgentReportAutomationReview(),
apiClient.getAiAgentReportTruthActionabilityReview(),
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
apiClient.getAiAgentHostStatefulVersionInventory(),
@@ -399,6 +402,7 @@ export function AutomationInventoryTab() {
runtimeWriteGateReviewResult,
postWriteVerifierPackageResult,
runtimeVerifierEvidenceReviewResult,
reportAutomationReviewResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -427,6 +431,7 @@ export function AutomationInventoryTab() {
setRuntimeWriteGateReview(runtimeWriteGateReviewResult.status === 'fulfilled' ? runtimeWriteGateReviewResult.value : null)
setPostWriteVerifierPackage(postWriteVerifierPackageResult.status === 'fulfilled' ? postWriteVerifierPackageResult.value : null)
setRuntimeVerifierEvidenceReview(runtimeVerifierEvidenceReviewResult.status === 'fulfilled' ? runtimeVerifierEvidenceReviewResult.value : null)
setReportAutomationReview(reportAutomationReviewResult.status === 'fulfilled' ? reportAutomationReviewResult.value : null)
setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null)
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
@@ -453,6 +458,7 @@ export function AutomationInventoryTab() {
runtimeWriteGateReviewResult,
postWriteVerifierPackageResult,
runtimeVerifierEvidenceReviewResult,
reportAutomationReviewResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -840,6 +846,24 @@ export function AutomationInventoryTab() {
})
}, [runtimeVerifierEvidenceReview])
const visibleReportCharts = useMemo(() => {
if (!reportAutomationReview) return []
return reportAutomationReview.report_charts.slice(0, 4)
}, [reportAutomationReview])
const visibleReportRecommendations = useMemo(() => {
if (!reportAutomationReview) return []
const priority = { critical: 0, high: 0, medium: 1, low: 2 } as Record<string, number>
return [...reportAutomationReview.analysis_recommendations]
.sort((a, b) => {
const left = priority[a.risk_tier] ?? 3
const right = priority[b.risk_tier] ?? 3
if (left !== right) return left - right
return Number(b.approval_required) - Number(a.approval_required)
})
.slice(0, 5)
}, [reportAutomationReview])
const visibleReportTruthFindings = useMemo(() => {
if (!reportTruthActionabilityReview) return []
const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record<string, number>
@@ -1059,7 +1083,7 @@ export function AutomationInventoryTab() {
)
}
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -1198,6 +1222,16 @@ export function AutomationInventoryTab() {
const runtimeVerifierApprovals = runtimeVerifierEvidenceReview.rollups.approval_required_action_ids.length
const runtimeVerifierBlockedActions = runtimeVerifierEvidenceReview.rollups.blocked_runtime_action_count
const runtimeVerifierLiveTotal = runtimeVerifierEvidenceReview.rollups.live_verifier_execution_count
const reportAutomationOverall = reportAutomationReview.program_status.overall_completion_percent
const reportCadenceCount = reportAutomationReview.rollups.report_cadence_count
const reportAgentCount = reportAutomationReview.rollups.agent_count
const reportWorkloadTotal = reportAutomationReview.rollups.workload_unit_total
const reportWorkloadDone = reportAutomationReview.rollups.workload_done_total
const reportRecommendations = reportAutomationReview.rollups.recommendation_count
const reportApprovalRecommendations = reportAutomationReview.rollups.approval_required_recommendation_ids.length
const reportAutoEnabled = reportAutomationReview.rollups.current_auto_execution_enabled_count
const reportLiveDelivery = reportAutomationReview.rollups.live_report_delivery_count
const reportLiveOptimization = reportAutomationReview.rollups.live_auto_optimization_count
const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent
const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count
const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count
@@ -1793,6 +1827,176 @@ export function AutomationInventoryTab() {
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #cdd9c4', borderRadius: 7, background: '#fbfdf8', display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>
<FileText size={14} style={{ color: '#16a34a' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('reportAutomationReview.title')}
</span>
</div>
<Chip
value={t('reportAutomationReview.source', {
generated: formatDateTime(reportAutomationReview.generated_at),
current: reportAutomationReview.program_status.current_task_id,
next: reportAutomationReview.program_status.next_task_id,
})}
muted
/>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(128px, 1fr))', gap: 10 }} className="automation-inventory-live-read-kpi-grid">
<MetricCard label={t('reportAutomationReview.metrics.overall')} value={`${reportAutomationOverall}%`} tone="ok" icon={<Gauge size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.cadences')} value={reportCadenceCount} tone="ok" icon={<RefreshCw size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.agents')} value={reportAgentCount} tone="ok" icon={<Boxes size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.workload')} value={reportWorkloadTotal} tone="warn" icon={<Layers3 size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.done')} value={reportWorkloadDone} tone="ok" icon={<ShieldCheck size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.recommendations')} value={reportRecommendations} tone="warn" icon={<Target size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.approval')} value={reportApprovalRecommendations} tone={reportApprovalRecommendations > 0 ? 'danger' : 'ok'} icon={<Lock size={16} />} />
<MetricCard label={t('reportAutomationReview.metrics.autoEnabled')} value={reportAutoEnabled} tone={reportAutoEnabled === 0 ? 'warn' : 'ok'} icon={<Database size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 12 }} className="automation-inventory-live-read-grid">
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<div style={{ padding: 11, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('reportAutomationReview.truthTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{reportAutomationReview.report_truth.truth_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('reportAutomationReview.flags.daily', { value: String(reportAutomationReview.report_truth.daily_report_ready) })} />
<Chip value={t('reportAutomationReview.flags.weekly', { value: String(reportAutomationReview.report_truth.weekly_report_ready) })} />
<Chip value={t('reportAutomationReview.flags.monthly', { value: String(reportAutomationReview.report_truth.monthly_report_ready) })} />
<Chip value={t('reportAutomationReview.flags.delivery', { value: reportLiveDelivery })} muted />
<Chip value={t('reportAutomationReview.flags.optimization', { value: reportLiveOptimization })} muted />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('reportAutomationReview.policyTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{reportAutomationReview.risk_tier_policy.policy_summary}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('reportAutomationReview.flags.highApproval', { value: String(reportAutomationReview.report_truth.high_risk_requires_approval) })} />
<Chip value={t('reportAutomationReview.flags.mediumLowPolicy', { value: String(reportAutomationReview.report_truth.medium_low_auto_policy_defined) })} muted />
<Chip value={t('reportAutomationReview.flags.mediumLowExecution', { value: String(reportAutomationReview.report_truth.medium_low_auto_execution_enabled) })} muted />
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 10 }}>
{reportAutomationReview.report_cadences.map(cadence => (
<div key={cadence.cadence_id} style={{ padding: 10, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center' }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413' }}>
{cadence.display_name}
</span>
<Chip value={redisDryRunValueLabel('agents', cadence.owner_agent)} muted />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{cadence.schedule}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('reportAutomationReview.labels.sections', { count: cadence.sections.length })} />
<Chip value={t('reportAutomationReview.labels.liveDelivery', { count: cadence.live_delivery_count })} muted />
</div>
</div>
))}
</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, minWidth: 0 }}>
<SmallLabel>{t('reportAutomationReview.workloadTitle')}</SmallLabel>
{reportAutomationReview.agent_workload_metrics.map(agent => {
const progress = agent.work_units_total > 0 ? Math.round((agent.work_units_done / agent.work_units_total) * 100) : 0
return (
<div key={agent.agent_id} style={{ padding: 10, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413' }}>
{agent.display_name}
</span>
<Chip value={t('reportAutomationReview.labels.workUnits', { count: agent.work_units_total })} />
</div>
<MiniBar
label={t('reportAutomationReview.labels.doneRatio')}
value={progress}
detail={t('reportAutomationReview.labels.doneDetail', {
done: agent.work_units_done,
total: agent.work_units_total,
approval: agent.work_units_waiting_approval,
})}
tone={progress >= 85 ? 'ok' : 'warn'}
/>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('reportAutomationReview.labels.targets', { count: agent.deployment_targets })} muted />
<Chip value={t('reportAutomationReview.labels.capabilities', { count: agent.delegable_capabilities })} muted />
<Chip value={t('reportAutomationReview.labels.liveRuntime', { count: agent.live_runtime_work_units_24h })} muted />
</div>
</div>
)
})}
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleReportCharts.map(chart => {
const maxValue = Math.max(...chart.series.map(item => item.value), 1)
return (
<div key={chart.chart_id} style={{ padding: 10, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center' }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413' }}>
{chart.display_name}
</span>
<Chip value={chart.chart_type} muted />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{chart.operator_question}
</span>
<div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
{chart.series.map(item => (
<div key={`${chart.chart_id}-${item.label}`} style={{ display: 'grid', gridTemplateColumns: '88px minmax(0, 1fr) 36px', gap: 8, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#605f59', overflowWrap: 'anywhere' }}>
{item.label}
</span>
<div style={{ height: 7, borderRadius: 999, background: '#e7e3d8', overflow: 'hidden' }}>
<div style={{ height: '100%', width: `${Math.max(4, Math.round((item.value / maxValue) * 100))}%`, borderRadius: 999, background: toneColor(item.tone) }} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, fontWeight: 700, color: toneColor(item.tone), textAlign: 'right' }}>
{item.value}
</span>
</div>
))}
</div>
</div>
)
})}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleReportRecommendations.map(recommendation => {
const tone = recommendation.risk_tier === 'high' || recommendation.risk_tier === 'critical' ? 'danger' : recommendation.risk_tier === 'medium' ? 'warn' : 'ok'
return (
<div key={recommendation.recommendation_id} style={{ padding: 10, border: '0.5px solid #e4eadf', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
{recommendation.display_name}
</span>
<Chip value={redisDryRunValueLabel('agents', recommendation.owner_agent)} muted />
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t(`reportAutomationReview.riskTiers.${recommendation.risk_tier}` as never)} muted={tone !== 'danger'} />
<Chip value={t('reportAutomationReview.labels.approvalRequired', { value: String(recommendation.approval_required) })} muted={!recommendation.approval_required} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{recommendation.proposed_solution}
</span>
<Chip value={recommendation.blocked_runtime_action} muted />
</div>
)
})}
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #d8c6a6', borderRadius: 7, background: '#fffdf7', display: 'flex', flexDirection: 'column', gap: 12, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 7, minWidth: 0 }}>

View File

@@ -322,6 +322,11 @@ export const apiClient = {
return handleResponse<AiAgentReportTruthActionabilityReviewSnapshot>(res)
},
async getAiAgentReportAutomationReview() {
const res = await fetch(`${API_BASE_URL}/agents/agent-report-automation-review`)
return handleResponse<AiAgentReportAutomationReviewSnapshot>(res)
},
async getAiAgentOwnerApprovedFixtureDryRun() {
const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`)
return handleResponse<AiAgentOwnerApprovedFixtureDryRunSnapshot>(res)
@@ -1948,6 +1953,127 @@ export interface AiAgentReportTruthActionabilityReviewSnapshot {
}
}
export interface AiAgentReportAutomationReviewSnapshot {
schema_version: 'ai_agent_report_automation_review_v1'
generated_at: string
program_status: {
overall_completion_percent: number
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
current_task_id: string
next_task_id: string
read_only_mode: true
runtime_authority: 'reporting_and_risk_policy_review_only_no_live_execution'
status_note: string
}
source_refs: string[]
report_truth: {
daily_report_ready: true
weekly_report_ready: true
monthly_report_ready: true
per_agent_workload_ready: true
chart_package_ready: true
report_delivery_enabled: false
report_delivery_count_24h: number
report_read_receipt_count_24h: number
ai_analysis_after_report_enabled: false
medium_low_auto_policy_defined: true
medium_low_auto_execution_enabled: false
live_medium_low_auto_execution_count_24h: number
high_risk_requires_approval: true
live_auto_optimization_count_24h: number
truth_note: string
}
report_cadences: Array<{
cadence_id: 'daily' | 'weekly' | 'monthly'
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
schedule: string
sections: string[]
chart_ids: string[]
delivery_channel: string
status: string
live_delivery_count: number
}>
agent_workload_metrics: Array<{
agent_id: 'openclaw' | 'hermes' | 'nemotron'
display_name: string
primary_role: string
deployment_targets: number
delegable_capabilities: number
report_owned_sections: number
analysis_owned_recommendations: number
work_units_total: number
work_units_done: number
work_units_waiting_approval: number
live_runtime_work_units_24h: number
workload_note: string
}>
report_charts: Array<{
chart_id: string
display_name: string
chart_type: string
unit: string
series: Array<{
label: string
value: number
tone: 'ok' | 'warn' | 'danger' | 'neutral'
}>
operator_question: string
}>
risk_tier_policy: {
policy_summary: string
risk_tiers: Array<{
risk_id: 'low' | 'medium' | 'high' | 'critical'
display_name: string
approval_required: boolean
auto_action_policy: string
current_execution_enabled: false
required_before_enable: string[]
}>
}
analysis_recommendations: Array<{
recommendation_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
risk_tier: 'low' | 'medium' | 'high' | 'critical'
approval_required: boolean
current_auto_execution_enabled: false
problem: string
proposed_solution: string
expected_report_signal: string
blocked_runtime_action: string
}>
approval_boundaries: Record<string, boolean>
display_redaction_contract: {
redaction_required: true
raw_report_payload_display_allowed: false
private_reasoning_display_allowed: false
secret_value_display_allowed: false
work_window_transcript_display_allowed: false
allowed_frontend_content: string[]
forbidden_frontend_content: string[]
frontend_display_policy: string
}
rollups: {
report_cadence_count: number
agent_count: number
chart_count: number
recommendation_count: number
workload_unit_total: number
workload_done_total: number
workload_waiting_approval_total: number
low_risk_recommendation_count: number
medium_risk_recommendation_count: number
high_risk_recommendation_count: number
critical_risk_recommendation_count: number
approval_required_recommendation_ids: string[]
medium_low_auto_policy_count: number
current_auto_execution_enabled_count: number
live_report_delivery_count: number
live_auto_optimization_count: number
}
}
export interface AiAgentOwnerApprovedFixtureDryRunSnapshot {
schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1'
generated_at: string