feat(governance): 新增 release decision readback
All checks were successful
Code Review / ai-code-review (push) Successful in 18s
CD Pipeline / tests (push) Successful in 1m42s
CD Pipeline / build-and-deploy (push) Successful in 5m12s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s

This commit is contained in:
Your Name
2026-06-14 11:43:03 +08:00
parent de884089d6
commit d41b1a383e
13 changed files with 1880 additions and 5 deletions

View File

@@ -14,6 +14,7 @@ import {
Archive,
BellOff,
BellRing,
BookOpenCheck,
Boxes,
CalendarClock,
Database,
@@ -86,6 +87,7 @@ import {
type AiAgentResultCaptureReleaseAuthorizationHoldSnapshot,
type AiAgentResultCaptureReleaseAuthorizationReadbackGateSnapshot,
type AiAgentResultCaptureReleaseDecisionHoldSnapshot,
type AiAgentResultCaptureReleaseDecisionReadbackSnapshot,
type AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot,
type AiAgentResultCaptureReleaseVerifierOwnerReviewPacketSnapshot,
type AiAgentResultCaptureWriterImplementationReviewSnapshot,
@@ -497,6 +499,7 @@ export function AutomationInventoryTab() {
const [resultCaptureReleaseVerifierPreflightGate, setResultCaptureReleaseVerifierPreflightGate] = useState<AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot | null>(null)
const [resultCaptureReleaseVerifierOwnerReviewPacket, setResultCaptureReleaseVerifierOwnerReviewPacket] = useState<AiAgentResultCaptureReleaseVerifierOwnerReviewPacketSnapshot | null>(null)
const [resultCaptureReleaseDecisionHold, setResultCaptureReleaseDecisionHold] = useState<AiAgentResultCaptureReleaseDecisionHoldSnapshot | null>(null)
const [resultCaptureReleaseDecisionReadback, setResultCaptureReleaseDecisionReadback] = useState<AiAgentResultCaptureReleaseDecisionReadbackSnapshot | null>(null)
const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState<AiAgentReportTruthActionabilityReviewSnapshot | null>(null)
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
@@ -572,6 +575,7 @@ export function AutomationInventoryTab() {
apiClient.getAiAgentResultCaptureReleaseVerifierPreflightGate(),
apiClient.getAiAgentResultCaptureReleaseVerifierOwnerReviewPacket(),
apiClient.getAiAgentResultCaptureReleaseDecisionHold(),
apiClient.getAiAgentResultCaptureReleaseDecisionReadback(),
apiClient.getAiAgentReportTruthActionabilityReview(),
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
apiClient.getAiAgentHostStatefulVersionInventory(),
@@ -646,6 +650,7 @@ export function AutomationInventoryTab() {
resultCaptureReleaseVerifierPreflightGateResult,
resultCaptureReleaseVerifierOwnerReviewPacketResult,
resultCaptureReleaseDecisionHoldResult,
resultCaptureReleaseDecisionReadbackResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -717,6 +722,7 @@ export function AutomationInventoryTab() {
setResultCaptureReleaseVerifierPreflightGate(resultCaptureReleaseVerifierPreflightGateResult.status === 'fulfilled' ? resultCaptureReleaseVerifierPreflightGateResult.value : null)
setResultCaptureReleaseVerifierOwnerReviewPacket(resultCaptureReleaseVerifierOwnerReviewPacketResult.status === 'fulfilled' ? resultCaptureReleaseVerifierOwnerReviewPacketResult.value : null)
setResultCaptureReleaseDecisionHold(resultCaptureReleaseDecisionHoldResult.status === 'fulfilled' ? resultCaptureReleaseDecisionHoldResult.value : null)
setResultCaptureReleaseDecisionReadback(resultCaptureReleaseDecisionReadbackResult.status === 'fulfilled' ? resultCaptureReleaseDecisionReadbackResult.value : null)
setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null)
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
@@ -786,6 +792,7 @@ export function AutomationInventoryTab() {
resultCaptureReleaseVerifierPreflightGateResult,
resultCaptureReleaseVerifierOwnerReviewPacketResult,
resultCaptureReleaseDecisionHoldResult,
resultCaptureReleaseDecisionReadbackResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -2028,7 +2035,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 || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !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 || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -3353,6 +3360,46 @@ export function AutomationInventoryTab() {
+ resultCaptureReleaseDecisionHold.rollups.playbook_trust_write_count
+ resultCaptureReleaseDecisionHold.rollups.production_write_count
)
const resultCaptureReleaseDecisionReadbackOverall = resultCaptureReleaseDecisionReadback.program_status.overall_completion_percent
const resultCaptureReleaseDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.release_decision_readback_count
const resultCaptureReleaseOwnerDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.owner_decision_readback_count
const resultCaptureReleaseVerifierDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.verifier_decision_readback_count
const resultCaptureReleaseRollbackDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.rollback_decision_readback_count
const resultCaptureReleaseMaintenanceDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.maintenance_window_decision_readback_count
const resultCaptureReleaseLiveApplyDecisionReadbacks = resultCaptureReleaseDecisionReadback.rollups.live_apply_decision_readback_count
const resultCaptureReleaseDecisionReadbackBlockedTransitions = resultCaptureReleaseDecisionReadback.rollups.blocked_readback_transition_count
const resultCaptureReleaseDecisionReadbackActions = resultCaptureReleaseDecisionReadback.rollups.operator_action_count
const resultCaptureReleaseDecisionReadbackApprovalRequired = (
resultCaptureReleaseDecisionReadback.rollups.approval_required_release_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.approval_required_owner_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.approval_required_verifier_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.approval_required_rollback_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.approval_required_maintenance_window_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.approval_required_live_apply_decision_count
)
const resultCaptureReleaseDecisionReadbackBlocked = (
resultCaptureReleaseDecisionReadback.rollups.blocked_release_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.blocked_owner_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.blocked_verifier_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.blocked_rollback_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.blocked_maintenance_window_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.blocked_live_apply_decision_count
+ resultCaptureReleaseDecisionReadback.rollups.critical_blocker_count
)
const resultCaptureReleaseDecisionReadbackLiveWrites = (
resultCaptureReleaseDecisionReadback.rollups.writer_apply_count
+ resultCaptureReleaseDecisionReadback.rollups.execution_apply_count
+ resultCaptureReleaseDecisionReadback.rollups.receipt_write_count
+ resultCaptureReleaseDecisionReadback.rollups.reviewer_queue_write_count
+ resultCaptureReleaseDecisionReadback.rollups.gateway_queue_write_count
+ resultCaptureReleaseDecisionReadback.rollups.telegram_send_count
+ resultCaptureReleaseDecisionReadback.rollups.bot_api_call_count
+ resultCaptureReleaseDecisionReadback.rollups.report_receipt_write_count
+ resultCaptureReleaseDecisionReadback.rollups.result_capture_write_count
+ resultCaptureReleaseDecisionReadback.rollups.learning_write_count
+ resultCaptureReleaseDecisionReadback.rollups.playbook_trust_write_count
+ resultCaptureReleaseDecisionReadback.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
@@ -8087,6 +8134,160 @@ export function AutomationInventoryTab() {
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #aebfd8', borderRadius: 7, background: '#f8fbff', 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 }}>
<BookOpenCheck size={14} style={{ color: '#275e9c' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('resultCaptureReleaseDecisionReadback.title')}
</span>
</div>
<Chip
value={t('resultCaptureReleaseDecisionReadback.source', {
generated: formatDateTime(resultCaptureReleaseDecisionReadback.generated_at),
current: resultCaptureReleaseDecisionReadback.program_status.current_task_id,
next: resultCaptureReleaseDecisionReadback.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('resultCaptureReleaseDecisionReadback.metrics.overall')} value={`${resultCaptureReleaseDecisionReadbackOverall}%`} tone="ok" icon={<Gauge size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.releaseReadbacks')} value={resultCaptureReleaseDecisionReadbacks} tone="warn" icon={<BookOpenCheck size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.ownerReadbacks')} value={resultCaptureReleaseOwnerDecisionReadbacks} tone="warn" icon={<ShieldCheck size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.verifierReadbacks')} value={resultCaptureReleaseVerifierDecisionReadbacks} tone="warn" icon={<ShieldAlert size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.rollbackReadbacks')} value={resultCaptureReleaseRollbackDecisionReadbacks} tone="warn" icon={<Undo2 size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.maintenanceReadbacks')} value={resultCaptureReleaseMaintenanceDecisionReadbacks} tone="warn" icon={<CalendarClock size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.liveApplyReadbacks')} value={resultCaptureReleaseLiveApplyDecisionReadbacks} tone="warn" icon={<Lock size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.blockers')} value={resultCaptureReleaseDecisionReadbackBlockedTransitions} tone="danger" icon={<AlertTriangle size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.actions')} value={resultCaptureReleaseDecisionReadbackActions} tone="warn" icon={<Target size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.approvalRequired')} value={resultCaptureReleaseDecisionReadbackApprovalRequired} tone="warn" icon={<ShieldAlert size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.blocked')} value={resultCaptureReleaseDecisionReadbackBlocked} tone="danger" icon={<ShieldAlert size={16} />} />
<MetricCard label={t('resultCaptureReleaseDecisionReadback.metrics.liveWrites')} value={resultCaptureReleaseDecisionReadbackLiveWrites} tone={resultCaptureReleaseDecisionReadbackLiveWrites === 0 ? 'ok' : 'danger'} icon={<BellOff size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: 10 }}>
<div style={{ padding: 10, border: '0.5px solid #d7c7a1', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('resultCaptureReleaseDecisionReadback.priorTitle')}</SmallLabel>
<p style={{ margin: '6px 0 8px', fontFamily: "'DM Mono', monospace", fontSize: 10, lineHeight: 1.5, color: '#5c5a55', overflowWrap: 'anywhere' }}>
{resultCaptureReleaseDecisionReadback.prior_release_decision_hold.readiness_note}
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={resultCaptureReleaseDecisionReadback.prior_release_decision_hold.schema_version} />
<Chip value={t('resultCaptureReleaseDecisionReadback.metrics.releaseReadbacks') + ` ${resultCaptureReleaseDecisionReadbacks}`} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.metrics.liveWrites') + ` ${resultCaptureReleaseDecisionReadback.prior_release_decision_hold.result_capture_write_count}`} muted />
</div>
</div>
<div style={{ padding: 10, border: '0.5px solid #d7c7a1', borderRadius: 7, background: '#fff', minWidth: 0 }}>
<SmallLabel>{t('resultCaptureReleaseDecisionReadback.truthTitle')}</SmallLabel>
<p style={{ margin: '6px 0 8px', fontFamily: "'DM Mono', monospace", fontSize: 10, lineHeight: 1.5, color: '#5c5a55', overflowWrap: 'anywhere' }}>
{resultCaptureReleaseDecisionReadback.release_decision_readback_truth.truth_note}
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('resultCaptureReleaseDecisionReadback.flags.p2Loaded', { value: String(resultCaptureReleaseDecisionReadback.release_decision_readback_truth.p2_138_release_decision_hold_loaded) })} />
<Chip value={t('resultCaptureReleaseDecisionReadback.flags.readbackReady', { value: String(resultCaptureReleaseDecisionReadback.release_decision_readback_truth.release_decision_readback_ready) })} />
<Chip value={t('resultCaptureReleaseDecisionReadback.flags.maintenanceStillRequired', { value: String(resultCaptureReleaseDecisionReadback.release_decision_readback_truth.maintenance_window_decision_still_required) })} />
<Chip value={t('resultCaptureReleaseDecisionReadback.flags.liveApplyStillRequired', { value: String(resultCaptureReleaseDecisionReadback.release_decision_readback_truth.live_apply_decision_still_required) })} />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.ownerReleaseAuthorized', { value: String(resultCaptureReleaseDecisionReadback.rollups.owner_release_authorized_count) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.decisionPassed', { value: String(resultCaptureReleaseDecisionReadback.rollups.release_decision_pass_count) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.gatewayWrites', { value: String(resultCaptureReleaseDecisionReadback.rollups.gateway_queue_write_count) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.telegramSends', { value: String(resultCaptureReleaseDecisionReadback.rollups.telegram_send_count) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.productionWrites', { value: String(resultCaptureReleaseDecisionReadback.rollups.production_write_count) })} muted />
</div>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{resultCaptureReleaseDecisionReadback.release_decision_readbacks.slice(0, 5).map(readback => (
<div key={readback.readback_id} style={{ padding: 10, border: '0.5px solid #aebfd8', 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' }}>
{readback.display_name}
</span>
<Chip value={readback.readback_status} muted={readback.readback_status === 'readback_complete'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{readback.readback_summary}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', readback.owner_agent)} muted />
<Chip value={readback.readback_mode} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.decisionPassed', { value: String(readback.release_decision_passed) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.authorizationGranted', { value: String(readback.release_authorization_granted) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.telegramSendAllowed', { value: String(readback.telegram_send_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">
{[
...resultCaptureReleaseDecisionReadback.owner_decision_readbacks.slice(0, 2),
...resultCaptureReleaseDecisionReadback.verifier_decision_readbacks.slice(0, 2),
...resultCaptureReleaseDecisionReadback.rollback_decision_readbacks.slice(0, 2),
...resultCaptureReleaseDecisionReadback.maintenance_window_decision_readbacks.slice(0, 2),
...resultCaptureReleaseDecisionReadback.live_apply_decision_readbacks.slice(0, 2),
].map(readback => (
<div key={readback.readback_id} style={{ padding: 10, border: '0.5px solid #aebfd8', 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' }}>
{readback.display_name}
</span>
<Chip value={readback.readback_status} muted={readback.readback_status === 'readback_complete'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{readback.readback_summary}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', readback.owner_agent)} muted />
<Chip value={readback.readback_mode} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.runtimeWriteAllowed', { value: String(readback.runtime_write_allowed) })} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.telegramSendAllowed', { value: String(readback.telegram_send_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">
{resultCaptureReleaseDecisionReadback.blocked_readback_transitions.slice(0, 6).map(transition => (
<div key={transition.transition_id} style={{ padding: 10, border: '0.5px solid #e0b4a3', 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' }}>
{transition.transition_id}
</span>
<Chip value={transition.status} muted />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{transition.reason}
</span>
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.runtimeWriteAllowed', { value: String(transition.runtime_write_allowed) })} muted />
</div>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{resultCaptureReleaseDecisionReadback.operator_actions.slice(0, 5).map(action => (
<div key={action.action_id} style={{ padding: 10, border: '0.5px solid #aebfd8', 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.action_id}
</span>
<Chip value={action.status} muted={action.status === 'ready_for_operator_review'} />
</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={redisDryRunValueLabel('agents', action.owner_agent)} muted />
<Chip value={t('resultCaptureReleaseDecisionReadback.labels.runtimeWriteAllowed', { value: String(action.runtime_write_allowed) })} muted />
</div>
</div>
))}
</div>
</div>
<div style={{ padding: 12, border: '0.5px solid #aebfd8', borderRadius: 7, background: '#f8fbff', 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

@@ -590,6 +590,11 @@ export const apiClient = {
return handleResponse<AiAgentResultCaptureReleaseDecisionHoldSnapshot>(res)
},
async getAiAgentResultCaptureReleaseDecisionReadback() {
const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-release-decision-readback`)
return handleResponse<AiAgentResultCaptureReleaseDecisionReadbackSnapshot>(res)
},
async getAiAgentOwnerApprovedFixtureDryRun() {
const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`)
return handleResponse<AiAgentOwnerApprovedFixtureDryRunSnapshot>(res)
@@ -9305,6 +9310,229 @@ export interface AiAgentResultCaptureReleaseDecisionHoldSnapshot {
}
}
interface AiAgentReleaseDecisionReadbackItem {
readback_id: string
display_name: string
owner_agent: string
source_hold_id: string
readback_status: string
readback_mode: string
readback_summary: string
release_decision_passed: boolean
release_authorization_granted: boolean
runtime_write_allowed: boolean
telegram_send_allowed: boolean
evidence_hash?: string
}
export interface AiAgentResultCaptureReleaseDecisionReadbackSnapshot {
schema_version: 'ai_agent_result_capture_release_decision_readback_v1'
generated_at: string
program_status: {
overall_completion_percent: number
current_priority: 'P0' | 'P1' | 'P2' | 'P3'
current_task_id: 'P2-139'
next_task_id: 'P2-140'
read_only_mode: true
runtime_authority: 'result_capture_release_decision_readback_only_no_live_write'
status_note: string
}
source_refs: string[]
prior_release_decision_hold: {
schema_version: string
release_decision_hold_count: number
owner_decision_hold_count: number
verifier_decision_hold_count: number
rollback_decision_hold_count: number
maintenance_window_decision_hold_count: number
live_apply_decision_hold_count: number
blocked_decision_transition_count: number
operator_action_count: number
approval_required_total: number
blocked_total: number
readiness_note: string
owner_release_authorized_count: number
owner_release_approved_count: number
owner_review_approved_count: number
owner_decision_approved_count: number
verifier_decision_approved_count: number
maintenance_window_approved_count: number
rollback_owner_confirmed_count: number
post_release_verifier_ready_count: number
final_release_candidate_approved_count: number
final_release_candidate_pass_count: number
release_decision_pass_count: number
release_authorization_granted_count: number
release_authorization_pass_count: number
rollback_release_pass_count: number
live_apply_release_pass_count: number
writer_apply_count: number
execution_apply_count: number
receipt_write_count: number
reviewer_queue_write_count: number
gateway_queue_write_count: number
telegram_send_count: number
bot_api_call_count: number
report_receipt_write_count: number
result_capture_write_count: number
learning_write_count: number
playbook_trust_write_count: number
production_write_count: number
secret_read_count: number
destructive_operation_count: number
}
release_decision_readback_truth: {
p2_138_release_decision_hold_loaded: boolean
release_decision_readback_ready: boolean
owner_decision_still_required: boolean
verifier_decision_still_required: boolean
rollback_decision_still_required: boolean
maintenance_window_decision_still_required: boolean
live_apply_decision_still_required: boolean
redaction_review_still_required: boolean
release_decision_readback_only: boolean
truth_note: string
owner_release_authorized: boolean
owner_release_approved: boolean
owner_review_approved: boolean
owner_decision_approved: boolean
verifier_decision_approved: boolean
maintenance_window_approved: boolean
rollback_owner_confirmed: boolean
post_release_verifier_ready: boolean
final_release_candidate_approved: boolean
final_release_candidate_passed: boolean
release_decision_passed: boolean
release_authorization_granted: boolean
release_authorization_passed: boolean
rollback_release_passed: boolean
live_apply_release_passed: boolean
writer_apply_enabled: boolean
execution_apply_enabled: boolean
receipt_write_enabled: boolean
reviewer_queue_write_enabled: boolean
gateway_queue_write_enabled: boolean
telegram_send_enabled: boolean
bot_api_call_enabled: boolean
report_receipt_write_enabled: boolean
result_capture_write_enabled: boolean
learning_write_enabled: boolean
playbook_trust_write_enabled: boolean
production_write_enabled: boolean
secret_read_enabled: boolean
destructive_operation_enabled: boolean
owner_release_authorized_count: number
owner_release_approved_count: number
owner_review_approved_count: number
owner_decision_approved_count: number
verifier_decision_approved_count: number
maintenance_window_approved_count: number
rollback_owner_confirmed_count: number
post_release_verifier_ready_count: number
final_release_candidate_approved_count: number
final_release_candidate_pass_count: number
release_decision_pass_count: number
release_authorization_granted_count: number
release_authorization_pass_count: number
rollback_release_pass_count: number
live_apply_release_pass_count: number
writer_apply_count: number
execution_apply_count: number
receipt_write_count: number
reviewer_queue_write_count: number
gateway_queue_write_count: number
telegram_send_count: number
bot_api_call_count: number
report_receipt_write_count: number
result_capture_write_count: number
learning_write_count: number
playbook_trust_write_count: number
production_write_count: number
secret_read_count: number
destructive_operation_count: number
}
release_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
owner_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
verifier_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
rollback_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
maintenance_window_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
live_apply_decision_readbacks: AiAgentReleaseDecisionReadbackItem[]
blocked_readback_transitions: {
transition_id: string
status: string
reason: string
runtime_write_allowed: boolean
}[]
operator_actions: {
action_id: string
owner_agent: string
status: string
operator_instruction: string
runtime_write_allowed: boolean
}[]
display_redaction_contract: {
redaction_required: boolean
raw_prompt_display_allowed: boolean
private_reasoning_display_allowed: boolean
secret_value_display_allowed: boolean
raw_runtime_payload_display_allowed: boolean
internal_collaboration_content_display_allowed: boolean
frontend_display_policy: string
}
rollups: {
release_decision_readback_count: number
owner_decision_readback_count: number
verifier_decision_readback_count: number
rollback_decision_readback_count: number
maintenance_window_decision_readback_count: number
live_apply_decision_readback_count: number
blocked_readback_transition_count: number
operator_action_count: number
approval_required_release_decision_count: number
blocked_release_decision_count: number
approval_required_owner_decision_count: number
blocked_owner_decision_count: number
approval_required_verifier_decision_count: number
blocked_verifier_decision_count: number
approval_required_rollback_decision_count: number
blocked_rollback_decision_count: number
approval_required_maintenance_window_decision_count: number
blocked_maintenance_window_decision_count: number
approval_required_live_apply_decision_count: number
blocked_live_apply_decision_count: number
critical_blocker_count: number
owner_release_authorized_count: number
owner_release_approved_count: number
owner_review_approved_count: number
owner_decision_approved_count: number
verifier_decision_approved_count: number
maintenance_window_approved_count: number
rollback_owner_confirmed_count: number
post_release_verifier_ready_count: number
final_release_candidate_approved_count: number
final_release_candidate_pass_count: number
release_decision_pass_count: number
release_authorization_granted_count: number
release_authorization_pass_count: number
rollback_release_pass_count: number
live_apply_release_pass_count: number
writer_apply_count: number
execution_apply_count: number
receipt_write_count: number
reviewer_queue_write_count: number
gateway_queue_write_count: number
telegram_send_count: number
bot_api_call_count: number
report_receipt_write_count: number
result_capture_write_count: number
learning_write_count: number
playbook_trust_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