Files
awoooi/apps/web/src/lib/api-client.ts
Your Name 4944d77093
All checks were successful
CD Pipeline / tests (push) Successful in 1m29s
Code Review / ai-code-review (push) Successful in 16s
CD Pipeline / build-and-deploy (push) Successful in 4m45s
CD Pipeline / post-deploy-checks (push) Successful in 1m31s
feat(governance): 新增監控合約降噪矩陣
2026-06-05 12:44:47 +08:00

1197 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AWOOOI API Client
* ADR-005: 所有請求經過 BFF
*
* 專案鐵律: 禁止任何 Fallback IP環境變數缺失即噴錯
*/
import { CURRENT_USER } from '@/lib/constants/user'
// 絕對純化: 環境變數缺失時直接拋出致命錯誤,嚴禁任何 Fallback
const getApiBaseUrl = (): string => {
const url = process.env.NEXT_PUBLIC_API_URL
if (!url) {
const fatalMsg = '[AWOOOI FATAL] Missing NEXT_PUBLIC_API_URL configuration.'
console.error(fatalMsg)
if (typeof window !== 'undefined') {
console.error('%c' + fatalMsg, 'color: #ef4444; font-weight: bold; font-size: 16px;')
}
throw new Error(fatalMsg)
}
return url.endsWith('/api/v1') ? url : `${url}/api/v1`
}
const API_BASE_URL = getApiBaseUrl()
export class ApiError extends Error {
constructor(
public status: number,
public code: string,
message: string
) {
super(message)
this.name = 'ApiError'
}
}
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const error = await response.json().catch(() => ({}))
throw new ApiError(
response.status,
error.code || 'UNKNOWN_ERROR',
error.message || response.statusText
)
}
return response.json()
}
export const apiClient = {
// Health
async getHealth() {
const res = await fetch(`${API_BASE_URL}/health`)
return handleResponse<{
status: 'healthy' | 'degraded' | 'unhealthy'
version: string
timestamp: string
components: Record<string, {
status: 'up' | 'down' | 'degraded'
latency_ms?: number | null
error?: string | null
}>
ollama_route_order?: string[]
}>(res)
},
// Agent
async getAgentStatus() {
const res = await fetch(`${API_BASE_URL}/agent/status`)
return handleResponse<{
status: 'idle' | 'thinking' | 'executing' | 'waiting_approval'
active_conversations: number
current_task: string | null
last_activity: string | null
}>(res)
},
async chat(message: string, conversationId?: string) {
const res = await fetch(`${API_BASE_URL}/agent/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, conversation_id: conversationId }),
})
return handleResponse<{
message: string
conversation_id: string
requires_approval: boolean
approval_id?: string
}>(res)
},
// Plugins
async listPlugins(category?: string) {
const params = category ? `?category=${category}` : ''
const res = await fetch(`${API_BASE_URL}/plugins${params}`)
return handleResponse<Array<{
id: string
name: string
version: string
category: string
enabled: boolean
description?: string
}>>(res)
},
// Approvals
async listApprovals(status?: string) {
const params = status ? `?status=${status}` : ''
const res = await fetch(`${API_BASE_URL}/approvals${params}`)
return handleResponse<{
items: Array<{
id: string
type: string
status: string
action: {
plugin_id: string
operation: string
risk_level: string
}
requested_at: string
}>
}>(res)
},
async signApproval(approvalId: string, signer: string = CURRENT_USER.id, comment?: string, csrfToken?: string | null) {
// Phase 22 P0: 加入 CSRF token + credentials (2026-03-31 Claude Code)
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (csrfToken) headers['X-CSRF-Token'] = csrfToken
const res = await fetch(`${API_BASE_URL}/approvals/${approvalId}/sign`, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
signer_id: signer,
signer_name: signer,
comment: comment,
}),
})
// 🔧 Fix: 回傳型別與後端實際結構對齊
return handleResponse<{
success: boolean
message: string
approval: ApprovalResponse
execution_triggered: boolean
// 向下相容舊欄位 (deprecated)
approval_id?: string
status?: string
current_signatures?: number
required_signatures?: number
}>(res)
},
async rejectApproval(approvalId: string, reason?: string, csrfToken?: string | null) {
// Phase 22 P0: 加入 CSRF token + credentials (2026-03-31 Claude Code)
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (csrfToken) headers['X-CSRF-Token'] = csrfToken
const res = await fetch(`${API_BASE_URL}/approvals/${approvalId}/reject`, {
method: 'POST',
headers,
credentials: 'include',
body: JSON.stringify({
rejector_id: CURRENT_USER.id,
rejector_name: CURRENT_USER.name,
reason: reason || 'Rejected via WarRoom',
}),
})
return handleResponse<{ id: string; status: string }>(res)
},
// =========================================================================
// Phase 7: Incidents API (真實血脈)
// =========================================================================
async listIncidents() {
const res = await fetch(`${API_BASE_URL}/incidents`)
return handleResponse<IncidentListResponse>(res)
},
async getIncident(incidentId: string) {
const res = await fetch(`${API_BASE_URL}/incidents/${incidentId}`)
return handleResponse<IncidentResponse>(res)
},
async getIncidentTimeline(incidentId: string) {
const res = await fetch(`${API_BASE_URL}/incidents/${incidentId}/timeline`)
return handleResponse<IncidentTimelineResponse>(res)
},
async generateProposal(incidentId: string) {
const res = await fetch(`${API_BASE_URL}/incidents/${incidentId}/proposal`, {
method: 'POST',
})
return handleResponse<ProposalGenerateResponse>(res)
},
// =========================================================================
// Phase 7: Pending Approvals API (真實血脈)
// =========================================================================
async getPendingApprovals() {
const res = await fetch(`${API_BASE_URL}/approvals/pending`)
return handleResponse<PendingApprovalsResponse>(res)
},
// =========================================================================
// Phase 10: Sentry Errors API (#40 BFF)
// =========================================================================
async getErrorStats() {
const res = await fetch(`${API_BASE_URL}/errors/stats`)
return handleResponse<ErrorStatsResponse>(res)
},
async listErrors(params?: { status?: string; level?: string; limit?: number }) {
const searchParams = new URLSearchParams()
if (params?.status) searchParams.set('status', params.status)
if (params?.level) searchParams.set('level', params.level)
if (params?.limit) searchParams.set('limit', params.limit.toString())
const query = searchParams.toString() ? `?${searchParams.toString()}` : ''
const res = await fetch(`${API_BASE_URL}/errors/issues${query}`)
return handleResponse<ErrorListResponse>(res)
},
async getErrorDetail(issueId: string) {
const res = await fetch(`${API_BASE_URL}/errors/issues/${issueId}`)
return handleResponse<ErrorDetailResponse>(res)
},
async getErrorTrends(period: '24h' | '7d' | '30d' = '24h') {
const res = await fetch(`${API_BASE_URL}/errors/trends?period=${period}`)
return handleResponse<ErrorTrendResponse>(res)
},
async analyzeError(issueId: string) {
const res = await fetch(`${API_BASE_URL}/errors/issues/${issueId}/analyze`, {
method: 'POST',
})
return handleResponse<ErrorAnalysisResponse>(res)
},
// =========================================================================
// Phase 19: UX Audit / Session Replay (#126)
// 2026-03-31 Claude Code - Frontend Replay UI Integration
// =========================================================================
async getUXAudit() {
const res = await fetch(`${API_BASE_URL}/errors/ux-audit`)
return handleResponse<UXAuditResponse>(res)
},
async getAgentMarketGovernanceSnapshot() {
const res = await fetch(`${API_BASE_URL}/agents/market-governance-snapshot`)
return handleResponse<AgentMarketGovernanceSnapshot>(res)
},
async getAiAgentAutomationInventorySnapshot() {
const res = await fetch(`${API_BASE_URL}/agents/automation-inventory-snapshot`)
return handleResponse<AiAgentAutomationInventorySnapshot>(res)
},
async getAiAgentAutomationBacklogSnapshot() {
const res = await fetch(`${API_BASE_URL}/agents/automation-backlog-snapshot`)
return handleResponse<AiAgentAutomationBacklogSnapshot>(res)
},
async getRuntimeSurfaceInventory() {
const res = await fetch(`${API_BASE_URL}/agents/runtime-surface-inventory`)
return handleResponse<RuntimeSurfaceInventorySnapshot>(res)
},
async getGiteaWorkflowRunnerHealth() {
const res = await fetch(`${API_BASE_URL}/agents/gitea-workflow-runner-health`)
return handleResponse<GiteaWorkflowRunnerHealthSnapshot>(res)
},
async getObservabilityContractMatrix() {
const res = await fetch(`${API_BASE_URL}/agents/observability-contract-matrix`)
return handleResponse<ObservabilityContractMatrixSnapshot>(res)
},
async getBackupDrTargetInventory() {
const res = await fetch(`${API_BASE_URL}/agents/backup-dr-target-inventory`)
return handleResponse<BackupDrTargetInventorySnapshot>(res)
},
async getBackupDrReadinessMatrix() {
const res = await fetch(`${API_BASE_URL}/agents/backup-dr-readiness-matrix`)
return handleResponse<BackupDrReadinessMatrixSnapshot>(res)
},
async getBackupNotificationPolicy() {
const res = await fetch(`${API_BASE_URL}/agents/backup-notification-policy`)
return handleResponse<BackupNotificationPolicySnapshot>(res)
},
async getOffsiteEscrowReadinessStatus() {
const res = await fetch(`${API_BASE_URL}/agents/offsite-escrow-readiness-status`)
return handleResponse<OffsiteEscrowReadinessStatusSnapshot>(res)
},
}
// =========================================================================
// Type Definitions (Phase 7)
// =========================================================================
/**
* Phase 6.5: 決策令牌資訊
* 確保 UI 永遠有決策可操作
*/
export interface DecisionInfo {
token: string
state: 'init' | 'analyzing' | 'ready' | 'executing' | 'completed' | 'error'
proposal_data: {
action: string
description: string
reasoning: string
risk_level: 'low' | 'medium' | 'critical'
kubectl_command: string
source: string
confidence: number
} | null
proposal_id: string | null
}
export interface IncidentResponse {
incident_id: string
status: 'investigating' | 'mitigating' | 'resolved' | 'closed'
severity: 'P0' | 'P1' | 'P2' | 'P3'
signal_count: number
affected_services: string[]
proposal_count: number
created_at: string
updated_at: string
/** Phase 6.5: 決策令牌 (確保 UI 永不鎖死) */
decision: DecisionInfo | null
}
export interface IncidentListResponse {
count: number
incidents: IncidentResponse[]
}
export interface IncidentTimelineEvent {
stage: string
status: string
title: string
description: string | null
actor: string | null
timestamp: string | null
source_table: string | null
data: Record<string, unknown>
}
export interface IncidentTimelineStage extends IncidentTimelineEvent {
label: string
events: IncidentTimelineEvent[]
}
export interface IncidentTimelineResponse {
incident_id: string
title: string
status: string
severity: string
started_at: string | null
updated_at: string | null
resolved_at: string | null
affected_services: string[]
approval_ids: string[]
timeline: IncidentTimelineStage[]
events: IncidentTimelineEvent[]
ascii_timeline: string
}
export interface BlastRadius {
affected_pods: number
estimated_downtime: string
related_services: string[]
data_impact: 'none' | 'read_only' | 'write' | 'destructive'
}
export interface DryRunCheck {
name: string
passed: boolean
message: string
}
export interface ApprovalResponse {
id: string
action: string
description: string
status: 'pending' | 'approved' | 'rejected' | 'expired'
risk_level: 'low' | 'medium' | 'high' | 'critical'
blast_radius: BlastRadius
dry_run_checks: DryRunCheck[]
required_signatures: number
current_signatures: number
signatures: Array<{ signer: string; signed_at: string }>
requested_by: string
created_at: string
expires_at: string | null
}
export interface PendingApprovalsResponse {
count: number
approvals: ApprovalResponse[]
}
export interface ProposalGenerateResponse {
success: boolean
message: string
incident_id: string
proposal: ApprovalResponse | null
incident_status: string | null
}
// =========================================================================
// Phase 10: Sentry Error Types (#40 BFF)
// =========================================================================
export interface SentryIssue {
id: string
short_id: string
title: string
culprit: string | null
level: 'error' | 'warning' | 'info' | 'fatal'
status: 'unresolved' | 'resolved' | 'ignored'
count: number
user_count: number
first_seen: string
last_seen: string
permalink: string | null
}
export interface ErrorStatsResponse {
total_issues: number
unresolved_issues: number
error_count_24h: number
critical_count: number
projects: string[]
}
export interface ErrorListResponse {
issues: SentryIssue[]
total: number
has_more: boolean
}
export interface ErrorDetailResponse {
issue: Record<string, unknown>
latest_event: Record<string, unknown> | null
sentry_url: string
}
export interface ErrorTrendPoint {
timestamp: string
count: number
}
export interface ErrorTrendResponse {
period: '24h' | '7d' | '30d'
data: ErrorTrendPoint[]
total_count: number
change_percent: number
}
export interface FixRecommendation {
summary: string
steps: string[]
code_suggestion: string | null
}
export interface PreventionMeasure {
type: string
description: string
}
export interface ErrorAnalysis {
root_cause: string
category: string
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
impact_assessment: string
fix_recommendation: FixRecommendation
prevention: PreventionMeasure[]
related_files: string[]
confidence: number
reasoning: string
}
export interface ErrorAnalysisResponse {
status: 'completed' | 'failed'
issue_id: string
provider: string
analysis?: ErrorAnalysis
analyzed_at?: string
sentry_url: string
message?: string
}
// =========================================================================
// Phase 19: UX Audit / Session Replay Types (#126)
// 2026-03-31 Claude Code - Frontend Replay UI Integration
// =========================================================================
export interface UXAuditDetail {
type: 'replay_with_errors' | 'ui_error'
replay_id?: string
issue_id?: string
url: string
error_count?: number
title?: string
count?: number
urls?: string[]
}
export interface UXAuditResponse {
replays_with_errors: number
rage_clicks: number
dead_clicks: number
ui_errors: number
health_score: 'good' | 'moderate' | 'poor'
details: UXAuditDetail[]
replay_dashboard_url: string
}
// =========================================================================
// Agent Market Governance Snapshot
// =========================================================================
export interface AgentMarketGovernanceSnapshot {
schema_version: 'agent_market_governance_snapshot_v1'
generated_at: string
current_decision: string
policy: Record<string, boolean>
evaluation_cadence: {
workflow: string
schedule: string
timezone: 'Asia/Taipei'
next_scheduled_run_at: string
trigger_modes: string[]
primary_source_policy: string
operator_review_gate: string
}
market_watch_health: {
status: 'healthy' | 'blocked'
freshness_sla_hours: 168
stale_grace_hours: 6
stale_after: string
source_failures_block_priority_upgrade: boolean
blocked_from_integration: number
operator_blockers: string[]
}
summary: {
candidate_count: number
source_count: number
source_failures: number
changed_candidates: number
integration_queue_count: number
blocked_from_integration: number
watch_only_candidates_reviewed: number
eligible_for_market_scorecard_prescreen: number
recommended_watch_additions_remaining: number
priority_upgrades_approved: number
market_scorecard_updates_approved: number
replay_candidates_approved: number
sdk_installations_approved: number
paid_api_calls_approved: number
production_changes_approved: number
shadow_or_canary_approved: number
replacement_decisions_approved: number
}
candidate_groups: {
production_baseline: string[]
replay_or_integration_blocked: string[]
watch_only_candidates: string[]
watch_only_scorecard_prescreen_ready: string[]
}
candidate_statuses: Array<{
candidate_id: string
display_name: string
role: string
evaluation_priority: string
gate_status:
| 'production_baseline'
| 'integration_blocked'
| 'integration_reviewed'
| 'watch_only_prescreen_ready'
| 'watch_only_blocked'
| 'watch_only_monitoring'
| 'registered_no_review'
current_gate: string
required_next_gate: string
integration_decision: string
score: number | null
evidence: {
latest_replay_summary: string | null
latest_smoke_gate: string | null
latest_smoke_matrix: string | null
latest_smoke_model: string | null
}
approvals: {
replay: false
sdk_install: false
paid_api: false
shadow_or_canary: false
production_routing: false
}
operator_blockers: string[]
}>
operator_decision_queue: Array<{
candidate_id: string
display_name: string
priority: number
queue_status:
| 'baseline_protected'
| 'blocked_needs_evidence'
| 'operator_review_required'
| 'operator_priority_review'
| 'watch_only_blocked'
| 'watch_only_monitoring'
| 'registered_no_review'
recommended_action: string
approval_boundary: {
replacement_adr_required: boolean
priority_upgrade_required: boolean
market_scorecard_update_required: boolean
replay_approval_required: boolean
sdk_install_approval_required: boolean
paid_api_approval_required: boolean
shadow_or_canary_approval_required: boolean
production_routing_approval_required: boolean
}
risk_notes: string[]
evidence_refs: string[]
}>
next_allowed_actions: string[]
forbidden_actions_without_new_approval: string[]
}
// =========================================================================
// AI Agent Automation Inventory Snapshot
// =========================================================================
export interface AiAgentAutomationInventorySnapshot {
schema_version: 'ai_agent_automation_inventory_snapshot_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
}
status_taxonomy: {
task_statuses: string[]
gate_statuses: string[]
priorities: Array<'P0' | 'P1' | 'P2' | 'P3'>
}
agent_roles: Array<{
agent_id: string
display_name: string
primary_role: string
allowed_actions: string[]
blocked_actions: string[]
}>
asset_domains: Array<{
domain_id: string
display_name: string
description: string
}>
assets: Array<{
asset_id: string
domain_id: string
display_name: string
asset_type: string
status: string
gate_status: string
owner_agent: string
risk_level: 'low' | 'medium' | 'high' | 'critical'
evidence_refs: string[]
next_action: string
}>
workstreams: Array<{
workstream_id: string
display_name: string
completion_percent: number
status: string
next_task_id: string
}>
tasks: Array<{
task_id: string
priority: 'P0' | 'P1' | 'P2' | 'P3'
status: string
completion_percent: number
owner_agent: string
title: string
output: string
gate_status: string
approval_boundary: {
mode: string
display_summary: string
allowed_actions: string[]
blocked_actions: string[]
requires_operator_approval_for: string[]
}
next_action: string
}>
task_approval_boundary_rollup: {
total_tasks: number
by_mode: Record<string, number>
tasks_requiring_explicit_approval: string[]
tasks_with_blocked_operations: string[]
}
evidence: Array<{
evidence_id: string
kind: 'schema' | 'test' | 'browser' | 'api' | 'build' | 'doc' | 'runtime'
ref: string
result: string
}>
approval_boundaries: Record<
| 'sdk_installation_allowed'
| 'paid_api_call_allowed'
| 'shadow_or_canary_allowed'
| 'production_routing_allowed'
| 'destructive_operation_allowed',
false
>
}
export interface AiAgentAutomationBacklogSnapshot {
schema_version: 'ai_agent_automation_backlog_v1'
generated_at: string
source_inventory_snapshot_ref: 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
}
rollups: {
total_items: number
by_priority: Record<string, number>
by_status: Record<string, number>
by_gate_status: Record<string, number>
by_owner_agent: Record<string, number>
}
progress_summary: {
overall_percent: number
done_items: number
planned_items: number
total_items: number
formula: string
by_priority: Array<{
priority: 'P0' | 'P1' | 'P2' | 'P3'
completion_percent: number
done_items: number
total_items: number
}>
by_workstream: Array<{
workstream_id: string
display_name: string
completion_percent: number
done_items: number
total_items: number
next_task_id: string
}>
}
backlog_items: Array<{
item_id: string
priority: 'P0' | 'P1' | 'P2' | 'P3'
status: string
workstream_id: string
source_asset_id: string
source_signal_kind: string
title: string
owner_agent: string
recommended_action: string
action_class: string
gate_status: string
risk_level: 'low' | 'medium' | 'high' | 'critical'
evidence_refs: string[]
acceptance_criteria: string[]
approval_boundary: {
mode: string
display_summary: string
allowed_actions: string[]
blocked_actions: string[]
requires_operator_approval_for: string[]
}
next_review: string
}>
item_approval_boundary_rollup: {
total_items: number
by_mode: Record<string, number>
items_requiring_explicit_approval: string[]
items_with_blocked_operations: string[]
}
approval_boundaries: Record<
| 'sdk_installation_allowed'
| 'paid_api_call_allowed'
| 'shadow_or_canary_allowed'
| 'production_routing_allowed'
| 'destructive_operation_allowed',
false
>
}
export interface RuntimeSurfaceInventorySnapshot {
schema_version: 'runtime_surface_inventory_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
}
source_refs: string[]
rollups: {
total_surfaces: number
by_kind: Record<string, number>
by_status: Record<string, number>
by_evidence_level: Record<string, number>
action_required_surface_ids: string[]
secret_surface_ids: string[]
live_check_missing_surface_ids: string[]
total_source_components: number
source_components_with_runtime_binding: number
}
runtime_surfaces: Array<{
surface_id: string
display_name: string
kind: 'deployment' | 'service' | 'ingress' | 'cronjob' | 'configmap' | 'secret' | 'rbac' | 'policy' | 'autoscaler' | 'availability'
manifest_ref: string
status: 'manifest_mapped' | 'action_required' | 'blocked' | 'missing'
risk_level: 'low' | 'medium' | 'high' | 'critical'
evidence_level: 'committed_manifest' | 'source_file' | 'missing_manifest' | 'live_check_required'
runtime_binding: string
health_contract: string
secret_exposure: 'none' | 'name_only' | 'template_only' | 'payload_redacted'
live_check_status: 'not_run' | 'not_applicable' | 'required'
evidence_refs: string[]
next_action: string
}>
source_runtime_components: Array<{
component_id: string
display_name: string
source_ref: string
component_kind: string
runtime_binding: string
status: 'bound' | 'action_required' | 'source_only'
next_action: string
}>
evidence_gaps: Array<{
gap_id: string
severity: 'low' | 'medium' | 'high' | 'critical'
status: 'action_required' | 'blocked' | 'accepted'
summary: string
evidence_refs: string[]
next_action: string
}>
operator_contract: {
display_mode: 'read_only_runtime_surface'
must_not_interpret_as: string[]
secret_display_policy: string
}
operation_boundaries: Record<string, boolean>
approval_boundaries: Record<string, false>
}
export interface GiteaWorkflowRunnerHealthSnapshot {
schema_version: 'gitea_workflow_runner_health_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
}
source_refs: string[]
rollups: {
total_workflows: number
by_workflow_status: Record<string, number>
by_runner_evidence_status: Record<string, number>
workflows_with_schedule: number
workflows_with_workflow_dispatch: number
workflows_with_notify_bridge: number
workflows_with_actionable_or_failure_quiet_policy: number
workflow_ids_requiring_runner_attestation: string[]
total_runner_contracts: number
runner_contracts_requiring_action: string[]
notification_contracts_total: number
notification_contracts_quiet_success_count: number
notification_contracts_quiet_success_ids: string[]
}
workflow_records: Array<{
workflow_id: string
file_ref: string
display_name: string
scope: string
status: 'manifest_mapped' | 'action_required' | 'blocked'
risk_level: 'low' | 'medium' | 'high' | 'critical'
triggers: string[]
schedule_cadence: string
runner_labels: string[]
runner_evidence_status: 'host_runner_mapped' | 'owner_attestation_required' | 'comment_ambiguous'
job_count: number
notification_policy: string
notify_bridge_calls: number
secrets_policy_status: string
evidence_refs: string[]
next_action: string
}>
runner_contracts: Array<{
contract_id: string
display_name: string
status: 'manifest_mapped' | 'action_required' | 'dry_run_only' | 'prepared_not_applied_by_snapshot'
risk_level: 'low' | 'medium' | 'high' | 'critical'
runner_labels: string[]
used_by_workflows: string[]
health_contract: string
guardrail_refs: string[]
evidence_refs: string[]
next_action: string
}>
notification_contracts: Array<{
contract_id: string
display_name: string
status: 'preserved' | 'exception_documented' | 'action_required'
policy_kind: 'failure_only' | 'actionable_only' | 'deployment_status_exception' | 'manual_status_exception' | 'read_only_no_notify'
success_noise_policy: string
failure_policy: string
workflow_refs: string[]
evidence_refs: string[]
next_action: string
}>
latest_observations: Array<{
observation_id: string
status: string
summary: string
evidence_refs: string[]
}>
operator_contract: {
display_mode: 'read_only_gitea_workflow_runner_health'
must_not_interpret_as: string[]
secret_display_policy: string
runner_mutation_policy: string
notification_policy: string
}
operation_boundaries: Record<string, boolean>
approval_boundaries: Record<string, false>
}
export interface ObservabilityContractMatrixSnapshot {
schema_version: 'observability_contract_matrix_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
}
source_refs: string[]
rollups: {
total_surfaces: number
by_kind: Record<string, number>
by_status: Record<string, number>
by_evidence_status: Record<string, number>
by_noise_policy_status: Record<string, number>
surface_ids_requiring_action: string[]
surface_ids_with_proposal_only_noise_policy: string[]
noise_reduction_opportunities_total: number
approval_required_opportunity_ids: string[]
classification_gap_ids: string[]
read_only_denials_total: number
}
observability_surfaces: Array<{
surface_id: string
display_name: string
kind: string
status: 'verified' | 'action_required' | 'blocked'
risk_level: 'low' | 'medium' | 'high' | 'critical'
evidence_status: string
noise_policy_status: string
coverage_contract: string
current_contract?: string
evidence_refs: string[]
next_action: string
}>
noise_reduction_opportunities: Array<{
opportunity_id: string
display_name: string
status: string
proposal_only: true
impact: string
target_surface_ids?: string[]
evidence_refs: string[]
next_action: string
}>
classification_gaps: Array<{
gap_id: string
display_name: string
status: string
severity: 'low' | 'medium' | 'high' | 'critical'
summary: string
evidence_refs: string[]
next_action: string
}>
latest_observations: Array<{
observation_id: string
status: string
summary: string
evidence_refs: string[]
}>
operator_contract: {
display_mode: 'read_only_observability_contract_matrix'
must_not_interpret_as: string[]
secret_display_policy: string
alertmanager_route_policy: string
noise_reduction_policy: string
notification_policy: string
}
operation_boundaries: Record<string, boolean>
approval_boundaries: Record<string, false>
}
export interface BackupDrTargetInventorySnapshot {
schema_version: 'backup_dr_target_inventory_v1'
generated_at: string
source_refs: 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
}
rollups: {
total_targets: number
by_status: Record<string, number>
by_target_type: Record<string, number>
by_gate_status: Record<string, number>
blocked_target_ids: string[]
}
backup_targets: Array<{
target_id: string
display_name: string
target_type: string
status: string
risk_level: 'low' | 'medium' | 'high' | 'critical'
owner_host: string
primary_script: string
schedule: string
rpo: string
storage_class: string
storage_ref: string
offsite_policy: string
automation_gate_status: string
restore_gate_status: string
secret_policy: string
evidence_refs: string[]
next_action: string
}>
approval_boundaries: Record<string, false>
operation_boundaries: Record<string, boolean>
}
export interface BackupDrReadinessMatrixSnapshot {
schema_version: 'backup_dr_readiness_matrix_v1'
generated_at: string
source_target_inventory_ref: string
source_refs: 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
}
rollups: {
total_rows: number
by_overall_readiness: Record<string, number>
by_restore_drill_status: Record<string, number>
by_offsite_status: Record<string, number>
blocked_row_ids: string[]
action_required_row_ids: string[]
}
readiness_rows: Array<{
target_id: string
display_name: string
overall_readiness: string
freshness_status: string
integrity_status: string
restore_drill_status: string
offsite_status: string
notification_policy: string
gate_status: string
evidence_level: string
evidence_refs: string[]
blocker_summary: string
next_action: string
}>
approval_boundaries: Record<string, false>
operation_boundaries: Record<string, boolean>
}
export interface BackupNotificationPolicySnapshot {
schema_version: 'backup_notification_policy_v1'
generated_at: string
source_readiness_matrix_ref: string
source_refs: 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
}
rollups: {
total_rules: number
by_decision: Record<string, number>
immediate_escalation_rule_ids: string[]
suppressed_success_rule_ids: string[]
}
notification_channels: Array<{
channel_id: string
purpose: string
immediate_allowed: boolean
success_immediate_allowed: boolean
requires_operator_action: boolean
}>
policy_rules: Array<{
rule_id: string
event_kind: string
backup_state: string
severity: string
decision: string
channels: string[]
owner_agent: string
requires_incident: boolean
requires_approval_record: boolean
message_contract: string
evidence_refs: string[]
}>
daily_summary_contract: Record<string, unknown>
approval_boundaries: Record<string, false>
operation_boundaries: Record<string, boolean>
}
export interface OffsiteEscrowReadinessStatusSnapshot {
schema_version: 'offsite_escrow_readiness_status_v1'
generated_at: string
source_refs: 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
}
rollups: {
total_cards: number
by_readiness: Record<string, number>
by_kind: Record<string, number>
verified_offsite_card_ids: string[]
blocked_escrow_card_ids: string[]
action_required_card_ids: string[]
execution_blocked_card_ids: string[]
}
readiness_cards: Array<{
card_id: string
target_id: string
display_name: string
kind: 'offsite_mirror' | 'credential_escrow' | 'k8s_resource_offsite'
readiness: 'verified' | 'action_required' | 'blocked'
offsite_status: string
escrow_status: string
restore_drill_status: string
credential_exposure_status: string
automation_gate_status: string
operator_summary: string
next_action: string
evidence_refs: string[]
blocked_operations: string[]
}>
operator_contract: {
display_mode: 'read_only_status'
success_notification_policy: string
failure_notification_policy: string
credential_display_policy: string
must_not_interpret_as: string[]
}
approval_boundaries: Record<string, false>
operation_boundaries: Record<string, boolean>
}