feat(governance): 新增 result capture writer dry-run 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 4m57s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s

This commit is contained in:
Your Name
2026-06-14 00:39:39 +08:00
parent c5cf6d3cc0
commit cdc6fe8737
10 changed files with 1605 additions and 1 deletions

View File

@@ -71,6 +71,7 @@ import {
type AiAgentResultCapturePromotionApprovalGateSnapshot,
type AiAgentResultCaptureWriteGateReviewSnapshot,
type AiAgentResultCaptureWriterDryRunFixtureSnapshot,
type AiAgentResultCaptureWriterDryRunReadbackSnapshot,
type AiAgentResultCaptureWriterImplementationReviewSnapshot,
type AiAgentOwnerApprovedFixturePromotionGateSnapshot,
type AiAgentRuntimeWorkerShadowGateSnapshot,
@@ -465,6 +466,7 @@ export function AutomationInventoryTab() {
const [resultCaptureWriteGateReview, setResultCaptureWriteGateReview] = useState<AiAgentResultCaptureWriteGateReviewSnapshot | null>(null)
const [resultCaptureWriterImplementationReview, setResultCaptureWriterImplementationReview] = useState<AiAgentResultCaptureWriterImplementationReviewSnapshot | null>(null)
const [resultCaptureWriterDryRunFixture, setResultCaptureWriterDryRunFixture] = useState<AiAgentResultCaptureWriterDryRunFixtureSnapshot | null>(null)
const [resultCaptureWriterDryRunReadback, setResultCaptureWriterDryRunReadback] = useState<AiAgentResultCaptureWriterDryRunReadbackSnapshot | null>(null)
const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState<AiAgentReportTruthActionabilityReviewSnapshot | null>(null)
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
@@ -525,6 +527,7 @@ export function AutomationInventoryTab() {
apiClient.getAiAgentResultCaptureWriteGateReview(),
apiClient.getAiAgentResultCaptureWriterImplementationReview(),
apiClient.getAiAgentResultCaptureWriterDryRunFixture(),
apiClient.getAiAgentResultCaptureWriterDryRunReadback(),
apiClient.getAiAgentReportTruthActionabilityReview(),
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
apiClient.getAiAgentHostStatefulVersionInventory(),
@@ -584,6 +587,7 @@ export function AutomationInventoryTab() {
resultCaptureWriteGateReviewResult,
resultCaptureWriterImplementationReviewResult,
resultCaptureWriterDryRunFixtureResult,
resultCaptureWriterDryRunReadbackResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -640,6 +644,7 @@ export function AutomationInventoryTab() {
setResultCaptureWriteGateReview(resultCaptureWriteGateReviewResult.status === 'fulfilled' ? resultCaptureWriteGateReviewResult.value : null)
setResultCaptureWriterImplementationReview(resultCaptureWriterImplementationReviewResult.status === 'fulfilled' ? resultCaptureWriterImplementationReviewResult.value : null)
setResultCaptureWriterDryRunFixture(resultCaptureWriterDryRunFixtureResult.status === 'fulfilled' ? resultCaptureWriterDryRunFixtureResult.value : null)
setResultCaptureWriterDryRunReadback(resultCaptureWriterDryRunReadbackResult.status === 'fulfilled' ? resultCaptureWriterDryRunReadbackResult.value : null)
setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null)
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
@@ -694,6 +699,7 @@ export function AutomationInventoryTab() {
resultCaptureWriteGateReviewResult,
resultCaptureWriterImplementationReviewResult,
resultCaptureWriterDryRunFixtureResult,
resultCaptureWriterDryRunReadbackResult,
reportTruthActionabilityReviewResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
@@ -1936,7 +1942,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 || !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 || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -2745,6 +2751,40 @@ export function AutomationInventoryTab() {
+ resultCaptureWriterDryRunFixture.rollups.playbook_trust_write_count
+ resultCaptureWriterDryRunFixture.rollups.production_write_count
)
const resultCaptureWriterReadbackOverall = resultCaptureWriterDryRunReadback.program_status.overall_completion_percent
const resultCaptureWriterReadbackCards = resultCaptureWriterDryRunReadback.rollups.dry_run_readback_card_count
const resultCaptureWriterReadbackVerifiers = resultCaptureWriterDryRunReadback.rollups.receipt_verifier_check_count
const resultCaptureWriterReadbackPromotionGates = resultCaptureWriterDryRunReadback.rollups.promotion_readiness_gate_count
const resultCaptureWriterReadbackBlockers = resultCaptureWriterDryRunReadback.rollups.blocked_promotion_write_count
const resultCaptureWriterReadbackActions = resultCaptureWriterDryRunReadback.rollups.operator_action_count
const resultCaptureWriterReadbackApprovalRequired = (
resultCaptureWriterDryRunReadback.rollups.approval_required_readback_count
+ resultCaptureWriterDryRunReadback.rollups.approval_required_verifier_count
+ resultCaptureWriterDryRunReadback.rollups.approval_required_gate_count
)
const resultCaptureWriterReadbackBlocked = (
resultCaptureWriterDryRunReadback.rollups.blocked_readback_count
+ resultCaptureWriterDryRunReadback.rollups.blocked_verifier_count
+ resultCaptureWriterDryRunReadback.rollups.blocked_gate_count
+ resultCaptureWriterDryRunReadback.rollups.critical_blocker_count
)
const resultCaptureWriterReadbackLiveWrites = (
resultCaptureWriterDryRunReadback.rollups.writer_apply_count
+ resultCaptureWriterDryRunReadback.rollups.dry_run_execution_count
+ resultCaptureWriterDryRunReadback.rollups.receipt_write_count
+ resultCaptureWriterDryRunReadback.rollups.promotion_apply_count
+ resultCaptureWriterDryRunReadback.rollups.canonical_runtime_target_read_count
+ resultCaptureWriterDryRunReadback.rollups.live_query_count
+ resultCaptureWriterDryRunReadback.rollups.reviewer_queue_write_count
+ resultCaptureWriterDryRunReadback.rollups.gateway_queue_write_count
+ resultCaptureWriterDryRunReadback.rollups.telegram_send_count
+ resultCaptureWriterDryRunReadback.rollups.bot_api_call_count
+ resultCaptureWriterDryRunReadback.rollups.report_receipt_write_count
+ resultCaptureWriterDryRunReadback.rollups.result_capture_write_count
+ resultCaptureWriterDryRunReadback.rollups.learning_write_count
+ resultCaptureWriterDryRunReadback.rollups.playbook_trust_write_count
+ resultCaptureWriterDryRunReadback.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
@@ -7294,6 +7334,110 @@ 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 }}>
<ShieldCheck size={14} style={{ color: '#275e9c' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('resultCaptureWriterDryRunReadback.title')}
</span>
</div>
<Chip
value={t('resultCaptureWriterDryRunReadback.source', {
generated: formatDateTime(resultCaptureWriterDryRunReadback.generated_at),
current: resultCaptureWriterDryRunReadback.program_status.current_task_id,
next: resultCaptureWriterDryRunReadback.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('resultCaptureWriterDryRunReadback.metrics.overall')} value={`${resultCaptureWriterReadbackOverall}%`} tone="ok" icon={<Gauge size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.readbacks')} value={resultCaptureWriterReadbackCards} tone="warn" icon={<Layers3 size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.verifiers')} value={resultCaptureWriterReadbackVerifiers} tone="warn" icon={<ShieldCheck size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.promotionGates')} value={resultCaptureWriterReadbackPromotionGates} tone="warn" icon={<Lock size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.blockers')} value={resultCaptureWriterReadbackBlockers} tone="danger" icon={<AlertTriangle size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.actions')} value={resultCaptureWriterReadbackActions} tone="warn" icon={<Target size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.approvalRequired')} value={resultCaptureWriterReadbackApprovalRequired} tone="warn" icon={<PackageCheck size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.blocked')} value={resultCaptureWriterReadbackBlocked} tone="danger" icon={<ShieldAlert size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.verifierPass')} value={resultCaptureWriterDryRunReadback.rollups.receipt_verifier_pass_count} tone="warn" icon={<Archive size={16} />} />
<MetricCard label={t('resultCaptureWriterDryRunReadback.metrics.liveWrites')} value={resultCaptureWriterReadbackLiveWrites} tone={resultCaptureWriterReadbackLiveWrites === 0 ? 'ok' : 'danger'} icon={<BellOff size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 12 }} className="automation-inventory-live-read-grid">
<div style={{ padding: 11, border: '0.5px solid #aebfd8', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('resultCaptureWriterDryRunReadback.priorTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{resultCaptureWriterDryRunReadback.prior_result_capture_writer_dry_run_fixture.readiness_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={resultCaptureWriterDryRunReadback.prior_result_capture_writer_dry_run_fixture.schema_version} />
<Chip value={t('resultCaptureWriterDryRunReadback.metrics.readbacks') + ` ${resultCaptureWriterDryRunReadback.rollups.dry_run_readback_card_count}`} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.metrics.liveWrites') + ` ${resultCaptureWriterDryRunReadback.prior_result_capture_writer_dry_run_fixture.result_capture_write_count}`} muted />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #aebfd8', borderRadius: 7, background: '#fff', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('resultCaptureWriterDryRunReadback.truthTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{resultCaptureWriterDryRunReadback.readback_truth.truth_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('resultCaptureWriterDryRunReadback.flags.fixtureLoaded', { value: String(resultCaptureWriterDryRunReadback.readback_truth.p2_123_fixture_loaded) })} />
<Chip value={t('resultCaptureWriterDryRunReadback.flags.receiptVerifier', { value: String(resultCaptureWriterDryRunReadback.readback_truth.receipt_verifier_required) })} />
<Chip value={t('resultCaptureWriterDryRunReadback.flags.promotionOwner', { value: String(resultCaptureWriterDryRunReadback.readback_truth.promotion_owner_required) })} />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.writerApply', { value: String(resultCaptureWriterDryRunReadback.rollups.writer_apply_count) })} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.dryRunExecution', { value: String(resultCaptureWriterDryRunReadback.rollups.dry_run_execution_count) })} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.receiptWrite', { value: String(resultCaptureWriterDryRunReadback.rollups.receipt_write_count) })} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.gatewayWrites', { value: String(resultCaptureWriterDryRunReadback.rollups.gateway_queue_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">
{resultCaptureWriterDryRunReadback.dry_run_readback_cards.slice(0, 5).map(card => (
<div key={card.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' }}>
{card.display_name}
</span>
<Chip value={card.status} muted={card.status === 'ready_for_owner_review'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{card.source_fixture} {card.receipt_preview}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', card.owner_agent)} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.promotionCandidate', { value: card.promotion_candidate })} />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.readbackOnly', { value: String(card.readback_only) })} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.runtimeWriteEnabled', { value: String(card.runtime_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">
{resultCaptureWriterDryRunReadback.promotion_readiness_gates.slice(0, 5).map(gate => (
<div key={gate.gate_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' }}>
{gate.display_name}
</span>
<Chip value={gate.status} muted={gate.status === 'ready_for_owner_review'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#64727a', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{gate.required_evidence}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', gate.owner_agent)} muted />
<Chip value={t('resultCaptureWriterDryRunReadback.labels.promotionAllowed', { value: String(gate.promotion_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 }}>