feat(governance): 新增 runtime readback 批准包
All checks were successful
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m29s
CD Pipeline / build-and-deploy (push) Successful in 4m36s
CD Pipeline / post-deploy-checks (push) Successful in 1m37s

This commit is contained in:
Your Name
2026-06-13 14:51:16 +08:00
parent e0cc7dde0f
commit 84fea85bf7
14 changed files with 2504 additions and 5 deletions

View File

@@ -57,6 +57,7 @@ import {
type AiAgentReportRuntimeFixtureReadbackSnapshot,
type AiAgentReportRuntimeReadinessSnapshot,
type AiAgentReportTruthActionabilityReviewSnapshot,
type AiAgentRuntimeReadbackApprovalPackageSnapshot,
type AiAgentRuntimeWorkerShadowGateSnapshot,
type AiAgentRuntimeVerifierEvidenceReviewSnapshot,
type AiAgentRuntimeWriteGateReviewSnapshot,
@@ -434,6 +435,7 @@ export function AutomationInventoryTab() {
const [criticReviewerResultCapture, setCriticReviewerResultCapture] = useState<AiAgentCriticReviewerResultCaptureSnapshot | null>(null)
const [ownerApprovedResultCaptureDryRun, setOwnerApprovedResultCaptureDryRun] = useState<AiAgentOwnerApprovedResultCaptureDryRunSnapshot | null>(null)
const [ownerApprovedResultCaptureReadback, setOwnerApprovedResultCaptureReadback] = useState<AiAgentOwnerApprovedResultCaptureReadbackSnapshot | null>(null)
const [runtimeReadbackApprovalPackage, setRuntimeReadbackApprovalPackage] = useState<AiAgentRuntimeReadbackApprovalPackageSnapshot | null>(null)
const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState<AiAgentReportTruthActionabilityReviewSnapshot | null>(null)
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
@@ -479,6 +481,7 @@ export function AutomationInventoryTab() {
apiClient.getAiAgentCriticReviewerResultCapture(),
apiClient.getAiAgentOwnerApprovedResultCaptureDryRun(),
apiClient.getAiAgentOwnerApprovedResultCaptureReadback(),
apiClient.getAiAgentRuntimeReadbackApprovalPackage(),
apiClient.getAiAgentReportTruthActionabilityReview(),
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
apiClient.getAiAgentHostStatefulVersionInventory(),
@@ -523,6 +526,7 @@ export function AutomationInventoryTab() {
criticReviewerResultCaptureResult,
ownerApprovedResultCaptureDryRunResult,
ownerApprovedResultCaptureReadbackResult,
runtimeReadbackApprovalPackageResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -564,6 +568,7 @@ export function AutomationInventoryTab() {
setCriticReviewerResultCapture(criticReviewerResultCaptureResult.status === 'fulfilled' ? criticReviewerResultCaptureResult.value : null)
setOwnerApprovedResultCaptureDryRun(ownerApprovedResultCaptureDryRunResult.status === 'fulfilled' ? ownerApprovedResultCaptureDryRunResult.value : null)
setOwnerApprovedResultCaptureReadback(ownerApprovedResultCaptureReadbackResult.status === 'fulfilled' ? ownerApprovedResultCaptureReadbackResult.value : null)
setRuntimeReadbackApprovalPackage(runtimeReadbackApprovalPackageResult.status === 'fulfilled' ? runtimeReadbackApprovalPackageResult.value : null)
setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null)
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
@@ -603,6 +608,7 @@ export function AutomationInventoryTab() {
criticReviewerResultCaptureResult,
ownerApprovedResultCaptureDryRunResult,
ownerApprovedResultCaptureReadbackResult,
runtimeReadbackApprovalPackageResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -1484,6 +1490,62 @@ export function AutomationInventoryTab() {
.slice(0, 4)
}, [ownerApprovedResultCaptureReadback])
const visibleRuntimeReadbackPackets = useMemo(() => {
if (!runtimeReadbackApprovalPackage) return []
const statusPriority = { blocked_by_policy: 0, approval_required: 1, ready_for_owner_review: 2 } as Record<string, number>
const riskPriority = { critical: 0, high: 1, medium: 2 } as Record<string, number>
return [...runtimeReadbackApprovalPackage.approval_packet_templates]
.sort((a, b) => {
const leftStatus = statusPriority[a.status] ?? 3
const rightStatus = statusPriority[b.status] ?? 3
if (leftStatus !== rightStatus) return leftStatus - rightStatus
const leftRisk = riskPriority[a.risk_tier] ?? 4
const rightRisk = riskPriority[b.risk_tier] ?? 4
if (leftRisk !== rightRisk) return leftRisk - rightRisk
return a.packet_id.localeCompare(b.packet_id)
})
.slice(0, 5)
}, [runtimeReadbackApprovalPackage])
const visibleRuntimeReadbackPlans = useMemo(() => {
if (!runtimeReadbackApprovalPackage) return []
const statusPriority = { blocked_by_policy: 0, planned_no_runtime_read: 1 } as Record<string, number>
return [...runtimeReadbackApprovalPackage.canonical_readback_plans]
.sort((a, b) => {
const left = statusPriority[a.status] ?? 2
const right = statusPriority[b.status] ?? 2
if (left !== right) return left - right
return a.plan_id.localeCompare(b.plan_id)
})
.slice(0, 4)
}, [runtimeReadbackApprovalPackage])
const visibleRuntimeReadbackRollbackLanes = useMemo(() => {
if (!runtimeReadbackApprovalPackage) return []
const statusPriority = { blocked_by_policy: 0, planned_no_write: 1 } as Record<string, number>
return [...runtimeReadbackApprovalPackage.rollback_drill_lanes]
.sort((a, b) => {
const left = statusPriority[a.status] ?? 2
const right = statusPriority[b.status] ?? 2
if (left !== right) return left - right
return a.lane_id.localeCompare(b.lane_id)
})
.slice(0, 4)
}, [runtimeReadbackApprovalPackage])
const visibleRuntimeReadbackTelegramGates = useMemo(() => {
if (!runtimeReadbackApprovalPackage) return []
const statusPriority = { blocked_by_policy: 0, ready_for_owner_review: 1 } as Record<string, number>
return [...runtimeReadbackApprovalPackage.telegram_failure_receipt_gates]
.sort((a, b) => {
const left = statusPriority[a.status] ?? 2
const right = statusPriority[b.status] ?? 2
if (left !== right) return left - right
return a.gate_id.localeCompare(b.gate_id)
})
.slice(0, 4)
}, [runtimeReadbackApprovalPackage])
const visibleReportTruthFindings = useMemo(() => {
if (!reportTruthActionabilityReview) return []
const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record<string, number>
@@ -1703,7 +1765,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 || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !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 || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -2024,6 +2086,35 @@ export function AutomationInventoryTab() {
+ ownerApprovedResultCaptureReadback.rollups.telegram_send_count
+ ownerApprovedResultCaptureReadback.rollups.production_write_count
)
const runtimeReadbackPackageOverall = runtimeReadbackApprovalPackage.program_status.overall_completion_percent
const runtimeReadbackPackets = runtimeReadbackApprovalPackage.rollups.approval_packet_count
const runtimeReadbackPlans = runtimeReadbackApprovalPackage.rollups.canonical_readback_plan_count
const runtimeReadbackRollbackLanes = runtimeReadbackApprovalPackage.rollups.rollback_drill_lane_count
const runtimeReadbackTelegramGates = runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_gate_count
const runtimeReadbackActions = runtimeReadbackApprovalPackage.rollups.operator_action_count
const runtimeReadbackApprovalRequired = runtimeReadbackApprovalPackage.rollups.approval_required_packet_count
const runtimeReadbackBlocked = (
runtimeReadbackApprovalPackage.rollups.blocked_packet_count
+ runtimeReadbackApprovalPackage.rollups.blocked_readback_plan_count
+ runtimeReadbackApprovalPackage.rollups.blocked_rollback_lane_count
+ runtimeReadbackApprovalPackage.rollups.blocked_telegram_gate_count
)
const runtimeReadbackOwnerApprovals = runtimeReadbackApprovalPackage.rollups.owner_approval_received_count
const runtimeReadbackExecutions = runtimeReadbackApprovalPackage.rollups.runtime_readback_execution_count
const runtimeReadbackTelegramSends = runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_send_count
const runtimeReadbackRollbackWrites = runtimeReadbackApprovalPackage.rollups.rollback_work_item_write_count
const runtimeReadbackLiveWrites = (
runtimeReadbackApprovalPackage.rollups.result_capture_write_count
+ runtimeReadbackApprovalPackage.rollups.score_write_count
+ runtimeReadbackApprovalPackage.rollups.learning_write_count
+ runtimeReadbackApprovalPackage.rollups.playbook_trust_write_count
+ runtimeReadbackApprovalPackage.rollups.reviewer_queue_write_count
+ runtimeReadbackApprovalPackage.rollups.gateway_queue_write_count
+ runtimeReadbackApprovalPackage.rollups.telegram_failure_receipt_send_count
+ runtimeReadbackApprovalPackage.rollups.bot_api_call_count
+ runtimeReadbackApprovalPackage.rollups.rollback_work_item_write_count
+ runtimeReadbackApprovalPackage.rollups.production_write_count
)
const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent
const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count
const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count
@@ -4790,6 +4881,187 @@ export function AutomationInventoryTab() {
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #bbdec8', borderRadius: 7, background: '#f8fffb', 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 }}>
<ShieldCheck size={14} style={{ color: '#15803d' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('runtimeReadbackApprovalPackage.title')}
</span>
</div>
<Chip
value={t('runtimeReadbackApprovalPackage.source', {
generated: formatDateTime(runtimeReadbackApprovalPackage.generated_at),
current: runtimeReadbackApprovalPackage.program_status.current_task_id,
next: runtimeReadbackApprovalPackage.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('runtimeReadbackApprovalPackage.metrics.overall')} value={`${runtimeReadbackPackageOverall}%`} tone="ok" icon={<Gauge size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.packets')} value={runtimeReadbackPackets} tone="warn" icon={<FileText size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.plans')} value={runtimeReadbackPlans} tone="warn" icon={<Database size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.rollback')} value={runtimeReadbackRollbackLanes} tone="danger" icon={<Route size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.telegramGates')} value={runtimeReadbackTelegramGates} tone="warn" icon={<BellOff size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.actions')} value={runtimeReadbackActions} tone="warn" icon={<Target size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.approvalRequired')} value={runtimeReadbackApprovalRequired} tone="warn" icon={<Lock size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.blocked')} value={runtimeReadbackBlocked} tone="danger" icon={<AlertTriangle size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.ownerApprovals')} value={runtimeReadbackOwnerApprovals} tone="warn" icon={<ShieldCheck size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.executions')} value={runtimeReadbackExecutions} tone={runtimeReadbackExecutions === 0 ? 'ok' : 'danger'} icon={<Gauge size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.telegramSends')} value={runtimeReadbackTelegramSends} tone={runtimeReadbackTelegramSends === 0 ? 'ok' : 'danger'} icon={<BellRing size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.rollbackWrites')} value={runtimeReadbackRollbackWrites} tone={runtimeReadbackRollbackWrites === 0 ? 'ok' : 'danger'} icon={<Database size={16} />} />
<MetricCard label={t('runtimeReadbackApprovalPackage.metrics.liveWrites')} value={runtimeReadbackLiveWrites} tone={runtimeReadbackLiveWrites === 0 ? 'ok' : 'danger'} 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={{ padding: 11, border: '0.5px solid #bbdec8', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('runtimeReadbackApprovalPackage.priorTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{runtimeReadbackApprovalPackage.prior_readback_readiness.readiness_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={runtimeReadbackApprovalPackage.prior_readback_readiness.source_schema_version} />
<Chip value={t('ownerApprovedResultCaptureReadback.metrics.digests') + ` ${runtimeReadbackApprovalPackage.prior_readback_readiness.readback_digest_count}`} muted />
<Chip value={t('ownerApprovedResultCaptureReadback.metrics.promotionReviews') + ` ${runtimeReadbackApprovalPackage.prior_readback_readiness.promotion_review_count}`} muted />
<Chip value={t('ownerApprovedResultCaptureReadback.metrics.liveWrites') + ` ${runtimeReadbackApprovalPackage.prior_readback_readiness.result_capture_write_count}`} muted />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #bbdec8', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('runtimeReadbackApprovalPackage.truthTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{runtimeReadbackApprovalPackage.approval_package_truth.truth_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('runtimeReadbackApprovalPackage.flags.readbackLoaded', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.p2_107_readback_loaded) })} />
<Chip value={t('runtimeReadbackApprovalPackage.flags.packageReady', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.approval_package_ready) })} />
<Chip value={t('runtimeReadbackApprovalPackage.flags.canonicalPlan', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.canonical_readback_plan_ready) })} />
<Chip value={t('runtimeReadbackApprovalPackage.flags.rollbackDrill', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.rollback_drill_ready) })} />
<Chip value={t('runtimeReadbackApprovalPackage.flags.failureReceipt', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.telegram_failure_receipt_gate_ready) })} />
<Chip value={t('runtimeReadbackApprovalPackage.flags.canonicalRead', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.canonical_runtime_readback_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.flags.resultWrite', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.runtime_result_capture_write_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.flags.queueWrite', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.reviewer_queue_write_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.flags.telegramSend', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.telegram_failure_receipt_send_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.flags.botApi', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.bot_api_call_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.flags.rollbackWrite', { value: String(runtimeReadbackApprovalPackage.approval_package_truth.rollback_work_item_write_enabled) })} muted />
</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleRuntimeReadbackPackets.map(packet => (
<div key={packet.packet_id} style={{ padding: 10, border: '0.5px solid #bbdec8', 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' }}>
{packet.display_name}
</span>
<Chip value={t(`runtimeReadbackApprovalPackage.packetStatuses.${packet.status}` as never)} muted={packet.status === 'ready_for_owner_review'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{packet.operator_guidance}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', packet.owner_agent)} muted />
<Chip value={t(`runtimeReadbackApprovalPackage.riskTiers.${packet.risk_tier}` as never)} muted={packet.risk_tier !== 'critical'} />
<Chip value={t('runtimeReadbackApprovalPackage.labels.approvalFields', { count: packet.required_approval_fields.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.blockedActions', { count: packet.blocked_runtime_actions.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.runtimeWrite', { value: String(packet.runtime_write_allowed) })} muted />
</div>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleRuntimeReadbackPlans.map(plan => (
<div key={plan.plan_id} style={{ padding: 10, border: '0.5px solid #bbdec8', 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' }}>
{plan.display_name}
</span>
<Chip value={t(`runtimeReadbackApprovalPackage.planStatuses.${plan.status}` as never)} muted={plan.status === 'planned_no_runtime_read'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{plan.query_scope}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', plan.owner_agent)} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.requiredFields', { count: plan.required_fields.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.redactionChecks', { count: plan.redaction_checks.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.liveQuery', { value: String(plan.live_query_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.runtimeWrite', { value: String(plan.runtime_write_allowed) })} muted />
</div>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleRuntimeReadbackRollbackLanes.map(lane => (
<div key={lane.lane_id} style={{ padding: 10, border: '0.5px solid #bbdec8', 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' }}>
{lane.display_name}
</span>
<Chip value={t(`runtimeReadbackApprovalPackage.rollbackStatuses.${lane.status}` as never)} muted={lane.status === 'planned_no_write'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{lane.failure_if_missing}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', lane.owner_agent)} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.rollbackOwner', { value: lane.rollback_owner })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.requiredSteps', { count: lane.required_steps.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.rollbackWrite', { value: String(lane.rollback_write_enabled) })} muted />
</div>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visibleRuntimeReadbackTelegramGates.map(gate => (
<div key={gate.gate_id} style={{ padding: 10, border: '0.5px solid #bbdec8', 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' }}>
{gate.display_name}
</span>
<Chip value={t(`runtimeReadbackApprovalPackage.telegramStatuses.${gate.status}` as never)} muted={gate.status === 'ready_for_owner_review'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{gate.failure_if_missing}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', gate.owner_agent)} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.requiredEvidence', { count: gate.required_evidence.length })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.queueWrite', { value: String(gate.queue_write_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.telegramSend', { value: String(gate.telegram_send_enabled) })} muted />
<Chip value={t('runtimeReadbackApprovalPackage.labels.botApi', { value: String(gate.bot_api_call_enabled) })} muted />
</div>
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{runtimeReadbackApprovalPackage.operator_actions.map(action => (
<div key={action.action_id} style={{ padding: 10, border: '0.5px solid #bbdec8', 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' }}>
{action.display_name}
</span>
<Chip value={redisDryRunValueLabel('agents', action.owner_agent)} muted />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{action.operator_instruction}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t(`runtimeReadbackApprovalPackage.actionTypes.${action.action_type}` as never)} />
<Chip value={t('runtimeReadbackApprovalPackage.labels.runtimeWrite', { value: String(action.runtime_write_allowed) })} muted />
</div>
</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

@@ -440,6 +440,11 @@ export const apiClient = {
return handleResponse<AiAgentOwnerApprovedResultCaptureReadbackSnapshot>(res)
},
async getAiAgentRuntimeReadbackApprovalPackage() {
const res = await fetch(`${API_BASE_URL}/agents/agent-runtime-readback-approval-package`)
return handleResponse<AiAgentRuntimeReadbackApprovalPackageSnapshot>(res)
},
async getAiAgentOwnerApprovedFixtureDryRun() {
const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`)
return handleResponse<AiAgentOwnerApprovedFixtureDryRunSnapshot>(res)
@@ -3803,6 +3808,175 @@ export interface AiAgentOwnerApprovedResultCaptureReadbackSnapshot {
}
}
export interface AiAgentRuntimeReadbackApprovalPackageSnapshot {
schema_version: 'ai_agent_runtime_readback_approval_package_v1'
generated_at: string
program_status: {
overall_completion_percent: number
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
current_task_id: 'P2-109'
next_task_id: 'P2-110'
read_only_mode: true
runtime_authority: 'runtime_readback_approval_package_only_no_live_write'
status_note: string
}
source_refs: string[]
prior_readback_readiness: {
source_schema_version: 'ai_agent_owner_approved_result_capture_readback_v1'
readback_at: string
readback_digest_count: number
promotion_review_count: number
failure_lane_count: number
reviewer_queue_preview_count: number
operator_action_count: number
approval_required_digest_count: number
blocked_total_count: number
approved_without_execution_meta_24h: number
execution_failed_with_matched_24h: number
owner_approval_received_count: number
readback_digest_generated_count: number
promotion_approved_count: number
reviewer_queue_write_count: number
result_capture_write_count: number
score_write_count: number
learning_write_count: number
playbook_trust_write_count: number
gateway_queue_write_count: number
telegram_send_count: number
production_write_count: number
readiness_note: string
}
approval_package_truth: {
p2_107_readback_loaded: true
approval_package_ready: true
canonical_readback_plan_ready: true
rollback_drill_ready: true
telegram_failure_receipt_gate_ready: true
owner_review_required_before_runtime: true
canonical_runtime_readback_enabled: false
runtime_result_capture_write_enabled: false
runtime_score_write_enabled: false
runtime_learning_write_enabled: false
playbook_trust_write_enabled: false
reviewer_queue_write_enabled: false
gateway_queue_write_enabled: false
telegram_failure_receipt_send_enabled: false
bot_api_call_enabled: false
rollback_work_item_write_enabled: false
production_write_enabled: false
secret_read_enabled: false
destructive_operation_enabled: false
owner_approval_received_count: number
runtime_readback_execution_count: number
result_capture_write_count_24h: number
score_write_count_24h: number
learning_write_count_24h: number
playbook_trust_write_count_24h: number
reviewer_queue_write_count_24h: number
gateway_queue_write_count_24h: number
telegram_failure_receipt_send_count_24h: number
bot_api_call_count_24h: number
rollback_work_item_write_count_24h: number
production_write_count_24h: number
secret_read_count_24h: number
destructive_operation_count_24h: number
truth_note: string
}
approval_packet_templates: Array<{
packet_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy'
risk_tier: 'medium' | 'high' | 'critical'
required_approval_fields: string[]
blocked_runtime_actions: string[]
operator_guidance: string
approval_required: true
runtime_write_allowed: false
evidence_hash: string
}>
canonical_readback_plans: Array<{
plan_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
status: 'planned_no_runtime_read' | 'blocked_by_policy'
query_scope: string
required_fields: string[]
redaction_checks: string[]
live_query_enabled: false
runtime_write_allowed: false
evidence_hash: string
}>
rollback_drill_lanes: Array<{
lane_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
status: 'planned_no_write' | 'blocked_by_policy'
rollback_owner: string
required_steps: string[]
failure_if_missing: string
rollback_write_enabled: false
creates_runtime_write: false
}>
telegram_failure_receipt_gates: Array<{
gate_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
status: 'ready_for_owner_review' | 'blocked_by_policy'
required_evidence: string[]
failure_if_missing: string
queue_write_enabled: false
telegram_send_enabled: false
bot_api_call_enabled: false
evidence_hash: string
}>
operator_actions: Array<{
action_id: string
action_type: 'review_approval_package' | 'validate_canonical_plan' | 'review_rollback_drill' | 'review_failure_receipt_gate' | 'reject_or_promote'
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
operator_instruction: string
runtime_write_allowed: false
}>
display_redaction_contract: {
redaction_required: true
frontend_display_policy: string
raw_prompt_display_allowed: false
private_reasoning_display_allowed: false
secret_value_display_allowed: false
raw_telegram_payload_display_allowed: false
work_window_transcript_display_allowed: false
}
rollups: {
approval_packet_count: number
canonical_readback_plan_count: number
rollback_drill_lane_count: number
telegram_failure_receipt_gate_count: number
operator_action_count: number
approval_required_packet_count: number
blocked_packet_count: number
blocked_readback_plan_count: number
blocked_rollback_lane_count: number
blocked_telegram_gate_count: number
approved_without_execution_meta_24h: number
execution_failed_with_matched_24h: number
owner_approval_received_count: number
runtime_readback_execution_count: number
result_capture_write_count: number
score_write_count: number
learning_write_count: number
playbook_trust_write_count: number
reviewer_queue_write_count: number
gateway_queue_write_count: number
telegram_failure_receipt_send_count: number
bot_api_call_count: number
rollback_work_item_write_count: number
production_write_count: number
secret_read_count: number
destructive_operation_count: number
}
}
export interface AiAgentOwnerApprovedFixtureDryRunSnapshot {
schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1'
generated_at: string