feat(governance): 新增 post-write verifier package
All checks were successful
CD Pipeline / tests (push) Successful in 1m28s
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / build-and-deploy (push) Successful in 5m34s
CD Pipeline / post-deploy-checks (push) Successful in 2m5s

This commit is contained in:
Your Name
2026-06-12 01:13:53 +08:00
parent 32fdce4cd9
commit 06b116c73f
21 changed files with 1245 additions and 39 deletions

View File

@@ -42,6 +42,7 @@ import {
type AiAgentLiveReadModelGateSnapshot,
type AiAgentOwnerApprovedFixtureDryRunSnapshot,
type AiAgentOwnerApprovedLearningDryRunSnapshot,
type AiAgentPostWriteVerifierPackageSnapshot,
type AiAgentProactiveOperationsContractSnapshot,
type AiAgentRedisDryRunGateSnapshot,
type AiAgentRuntimeWriteGateReviewSnapshot,
@@ -331,6 +332,7 @@ export function AutomationInventoryTab() {
const [telegramReceiptPackage, setTelegramReceiptPackage] = useState<AiAgentTelegramReceiptApprovalPackageSnapshot | null>(null)
const [ownerApprovedLearningDryRun, setOwnerApprovedLearningDryRun] = useState<AiAgentOwnerApprovedLearningDryRunSnapshot | null>(null)
const [runtimeWriteGateReview, setRuntimeWriteGateReview] = useState<AiAgentRuntimeWriteGateReviewSnapshot | null>(null)
const [postWriteVerifierPackage, setPostWriteVerifierPackage] = useState<AiAgentPostWriteVerifierPackageSnapshot | null>(null)
const [ownerDryRunPackage, setOwnerDryRunPackage] = useState<AiAgentOwnerApprovedFixtureDryRunSnapshot | null>(null)
const [hostStatefulInventory, setHostStatefulInventory] = useState<AiAgentHostStatefulVersionInventorySnapshot | null>(null)
const [serviceHealthGapMatrix, setServiceHealthGapMatrix] = useState<ServiceHealthGapMatrixSnapshot | null>(null)
@@ -360,6 +362,7 @@ export function AutomationInventoryTab() {
apiClient.getAiAgentTelegramReceiptApprovalPackage(),
apiClient.getAiAgentOwnerApprovedLearningDryRun(),
apiClient.getAiAgentRuntimeWriteGateReview(),
apiClient.getAiAgentPostWriteVerifierPackage(),
apiClient.getAiAgentOwnerApprovedFixtureDryRun(),
apiClient.getAiAgentHostStatefulVersionInventory(),
apiClient.getServiceHealthGapMatrix(),
@@ -388,6 +391,7 @@ export function AutomationInventoryTab() {
telegramReceiptPackageResult,
ownerApprovedLearningDryRunResult,
runtimeWriteGateReviewResult,
postWriteVerifierPackageResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
serviceHealthGapMatrixResult,
@@ -413,6 +417,7 @@ export function AutomationInventoryTab() {
setTelegramReceiptPackage(telegramReceiptPackageResult.status === 'fulfilled' ? telegramReceiptPackageResult.value : null)
setOwnerApprovedLearningDryRun(ownerApprovedLearningDryRunResult.status === 'fulfilled' ? ownerApprovedLearningDryRunResult.value : null)
setRuntimeWriteGateReview(runtimeWriteGateReviewResult.status === 'fulfilled' ? runtimeWriteGateReviewResult.value : null)
setPostWriteVerifierPackage(postWriteVerifierPackageResult.status === 'fulfilled' ? postWriteVerifierPackageResult.value : null)
setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null)
setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null)
setServiceHealthGapMatrix(serviceHealthGapMatrixResult.status === 'fulfilled' ? serviceHealthGapMatrixResult.value : null)
@@ -436,6 +441,7 @@ export function AutomationInventoryTab() {
telegramReceiptPackageResult,
ownerApprovedLearningDryRunResult,
runtimeWriteGateReviewResult,
postWriteVerifierPackageResult,
ownerDryRunPackageResult,
hostStatefulInventoryResult,
serviceHealthGapMatrixResult,
@@ -750,6 +756,42 @@ export function AutomationInventoryTab() {
})
}, [runtimeWriteGateReview])
const visiblePostWriteVerifierTargets = useMemo(() => {
if (!postWriteVerifierPackage) return []
const priority = { approval_required: 0, blocked_by_runtime_gate: 1, contract_ready: 2 } as Record<string, number>
return [...postWriteVerifierPackage.verification_targets]
.sort((a, b) => {
const left = priority[a.status] ?? 3
const right = priority[b.status] ?? 3
if (left !== right) return left - right
return a.target_id.localeCompare(b.target_id)
})
}, [postWriteVerifierPackage])
const visiblePostWriteVerifierLanes = useMemo(() => {
if (!postWriteVerifierPackage) return []
const priority = { approval_required: 0, blocked_by_runtime_gate: 1, contract_ready: 2 } as Record<string, number>
return [...postWriteVerifierPackage.failure_lanes]
.sort((a, b) => {
const left = priority[a.status] ?? 3
const right = priority[b.status] ?? 3
if (left !== right) return left - right
return a.lane_id.localeCompare(b.lane_id)
})
}, [postWriteVerifierPackage])
const visiblePostWriteVerifierActions = useMemo(() => {
if (!postWriteVerifierPackage) return []
const priority = { approval_required: 0, ready_for_owner: 1, blocked_by_runtime_gate: 2 } as Record<string, number>
return [...postWriteVerifierPackage.operator_actions]
.sort((a, b) => {
const left = priority[a.status] ?? 3
const right = priority[b.status] ?? 3
if (left !== right) return left - right
return a.action_id.localeCompare(b.action_id)
})
}, [postWriteVerifierPackage])
const visibleOwnerDryRunGates = useMemo(() => {
if (!ownerDryRunPackage) return []
const priority = { approval_required: 0, approved_for_fixture_only: 1, fixture_only: 2, ready: 3 } as Record<string, number>
@@ -929,7 +971,7 @@ export function AutomationInventoryTab() {
)
}
if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !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 || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) {
return (
<div style={{ padding: 20 }}>
<GlassCard variant="subtle" padding="lg">
@@ -1054,6 +1096,13 @@ export function AutomationInventoryTab() {
const runtimeWriteApprovals = runtimeWriteGateReview.rollups.approval_required_gate_ids.length
const runtimeWriteBlockedActions = runtimeWriteGateReview.rollups.blocked_runtime_action_count
const runtimeWriteLiveTotal = runtimeWriteGateReview.rollups.live_write_count_total
const postWriteVerifierOverall = postWriteVerifierPackage.program_status.overall_completion_percent
const postWriteVerifierTargets = postWriteVerifierPackage.rollups.verification_target_count
const postWriteVerifierFailureLanes = postWriteVerifierPackage.rollups.failure_lane_count
const postWriteVerifierActions = postWriteVerifierPackage.rollups.operator_action_count
const postWriteVerifierApprovals = postWriteVerifierPackage.rollups.approval_required_action_ids.length
const postWriteVerifierBlockedActions = postWriteVerifierPackage.rollups.blocked_runtime_action_count
const postWriteVerifierLiveTotal = postWriteVerifierPackage.rollups.live_verifier_execution_count
const ownerDryRunOverall = ownerDryRunPackage.program_status.overall_completion_percent
const ownerDryRunFixtures = ownerDryRunPackage.rollups.fixture_set_count
const ownerDryRunGates = ownerDryRunPackage.rollups.dry_run_gate_count
@@ -1745,6 +1794,111 @@ export function AutomationInventoryTab() {
</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 }}>
<PackageCheck size={14} style={{ color: '#d97757' }} />
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 13, fontWeight: 700, color: '#141413' }}>
{t('postWriteVerifierPackage.title')}
</span>
</div>
<Chip
value={t('postWriteVerifierPackage.source', {
generated: formatDateTime(postWriteVerifierPackage.generated_at),
current: postWriteVerifierPackage.program_status.current_task_id,
next: postWriteVerifierPackage.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('postWriteVerifierPackage.metrics.overall')} value={`${postWriteVerifierOverall}%`} tone="warn" icon={<Gauge size={16} />} />
<MetricCard label={t('postWriteVerifierPackage.metrics.targets')} value={postWriteVerifierTargets} tone="ok" icon={<Target size={16} />} />
<MetricCard label={t('postWriteVerifierPackage.metrics.lanes')} value={postWriteVerifierFailureLanes} tone="warn" icon={<GitBranch size={16} />} />
<MetricCard label={t('postWriteVerifierPackage.metrics.actions')} value={postWriteVerifierActions} tone="ok" icon={<FileText size={16} />} />
<MetricCard label={t('postWriteVerifierPackage.metrics.approval')} value={postWriteVerifierApprovals} tone={postWriteVerifierApprovals > 0 ? 'danger' : 'ok'} icon={<Lock size={16} />} />
<MetricCard label={t('postWriteVerifierPackage.metrics.live')} value={postWriteVerifierLiveTotal} tone={postWriteVerifierLiveTotal === 0 ? 'warn' : 'ok'} icon={<Database size={16} />} />
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 0.9fr) minmax(0, 1.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 #eee9dd', borderRadius: 7, background: '#faf9f3', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('postWriteVerifierPackage.packageTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{postWriteVerifierPackage.verifier_package.operator_meaning}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('postWriteVerifierPackage.labels.requiredInputs', { count: postWriteVerifierPackage.rollups.required_input_count })} />
<Chip value={t('postWriteVerifierPackage.labels.forbiddenInputs', { count: postWriteVerifierPackage.rollups.forbidden_input_count })} muted />
<Chip value={redisDryRunValueLabel('agents', postWriteVerifierPackage.verifier_package.owner_agent)} muted />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #eee9dd', borderRadius: 7, background: '#faf9f3', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('postWriteVerifierPackage.truthTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{postWriteVerifierPackage.verifier_truth.truth_note}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={t('postWriteVerifierPackage.flags.implemented', { value: String(postWriteVerifierPackage.verifier_truth.post_write_verifier_implemented) })} />
<Chip value={t('postWriteVerifierPackage.flags.canonical', { value: String(postWriteVerifierPackage.verifier_truth.canonical_readback_allowed) })} muted />
<Chip value={t('postWriteVerifierPackage.flags.rollback', { value: postWriteVerifierPackage.verifier_truth.rollback_work_item_created_count })} muted />
<Chip value={t('postWriteVerifierPackage.flags.telegram', { value: postWriteVerifierPackage.verifier_truth.telegram_failure_receipt_sent_count })} muted />
</div>
</div>
<div style={{ padding: 11, border: '0.5px solid #eee9dd', borderRadius: 7, background: '#faf9f3', display: 'flex', flexDirection: 'column', gap: 8, minWidth: 0 }}>
<SmallLabel>{t('postWriteVerifierPackage.failureTitle')}</SmallLabel>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.5, overflowWrap: 'anywhere' }}>
{postWriteVerifierPackage.verifier_package.failure_policy}
</span>
<Chip value={t('postWriteVerifierPackage.metrics.blocked', { count: postWriteVerifierBlockedActions })} muted />
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{visiblePostWriteVerifierTargets.map(target => (
<div key={target.target_id} style={{ padding: 10, border: '0.5px solid #eee9dd', borderRadius: 7, background: '#faf9f3', display: 'flex', flexDirection: 'column', gap: 7, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'center', minWidth: 0 }}>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 11, fontWeight: 700, color: '#141413', overflowWrap: 'anywhere' }}>
{target.target_id}
</span>
<Chip value={redisDryRunValueLabel('statuses', target.status)} muted={target.status === 'contract_ready'} />
</div>
<span style={{ fontFamily: 'Syne, sans-serif', fontSize: 12, fontWeight: 700, color: '#141413', lineHeight: 1.35 }}>
{target.display_name}
</span>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{target.operator_instruction}
</span>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
<Chip value={redisDryRunValueLabel('agents', target.owner_agent)} muted />
<Chip value={target.blocked_runtime_action} muted />
</div>
</div>
))}
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(190px, 1fr))', gap: 10 }} className="automation-inventory-live-read-card-grid">
{[...visiblePostWriteVerifierLanes, ...visiblePostWriteVerifierActions].map(item => (
<div key={'lane_id' in item ? item.lane_id : item.action_id} style={{ padding: 10, border: '0.5px solid #eee9dd', borderRadius: 7, background: '#faf9f3', 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' }}>
{item.display_name}
</span>
<Chip value={redisDryRunValueLabel('statuses', item.status)} muted={item.status !== 'approval_required'} />
</div>
<span style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#87867f', lineHeight: 1.45, overflowWrap: 'anywhere' }}>
{'required_evidence' in item ? item.required_evidence : item.operator_instruction}
</span>
<Chip value={item.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

@@ -307,6 +307,11 @@ export const apiClient = {
return handleResponse<AiAgentRuntimeWriteGateReviewSnapshot>(res)
},
async getAiAgentPostWriteVerifierPackage() {
const res = await fetch(`${API_BASE_URL}/agents/agent-post-write-verifier-package`)
return handleResponse<AiAgentPostWriteVerifierPackageSnapshot>(res)
},
async getAiAgentOwnerApprovedFixtureDryRun() {
const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`)
return handleResponse<AiAgentOwnerApprovedFixtureDryRunSnapshot>(res)
@@ -1675,6 +1680,88 @@ export interface AiAgentRuntimeWriteGateReviewSnapshot {
}
}
export interface AiAgentPostWriteVerifierPackageSnapshot {
schema_version: 'ai_agent_post_write_verifier_package_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: 'post_write_verifier_package_only_no_runtime_write'
status_note: string
}
source_refs: string[]
verifier_truth: {
runtime_write_allowed: false
post_write_verifier_implemented: false
post_write_verifier_executed_count: number
rollback_work_item_created_count: number
telegram_failure_receipt_sent_count: number
canonical_readback_allowed: false
truth_note: string
}
verifier_package: {
package_id: string
display_name: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
status: string
operator_meaning: string
required_inputs: string[]
forbidden_inputs: string[]
success_policy: string
failure_policy: string
}
verification_targets: Array<{
target_id: string
display_name: string
target_surface: string
status: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
required_readback: string
blocked_runtime_action: string
operator_instruction: string
}>
failure_lanes: Array<{
lane_id: string
display_name: string
status: string
required_evidence: string
blocked_runtime_action: string
operator_instruction: string
}>
operator_actions: Array<{
action_id: string
display_name: string
action_type: 'review' | 'collect_evidence' | 'approve_implementation' | 'reject_or_rework'
status: string
owner_agent: 'openclaw' | 'hermes' | 'nemotron'
operator_instruction: string
blocked_runtime_action: string
}>
approval_boundaries: Record<string, false>
display_redaction_contract: {
redaction_required: true
raw_payload_display_allowed: false
private_reasoning_display_allowed: false
secret_value_display_allowed: false
allowed_frontend_content: string[]
forbidden_frontend_content: string[]
frontend_display_policy: string
}
rollups: {
verification_target_count: number
failure_lane_count: number
operator_action_count: number
approval_required_action_ids: string[]
blocked_runtime_action_count: number
required_input_count: number
forbidden_input_count: number
live_verifier_execution_count: number
}
}
export interface AiAgentOwnerApprovedFixtureDryRunSnapshot {
schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1'
generated_at: string