#!/usr/bin/env python3
"""
IwoooS backup / restore / escrow / retention repo-only 清冊。

本工具只讀取已提交的 repo 檔案，整理 backup orchestration、service
backup scripts、restic retention、offsite sync、credential escrow、Velero
restore drill、alert rules 與 DR runbook evidence。它不執行 backup、不
restore、不呼叫 rclone、不寫 escrow marker、不跑 restic prune、不連
K8s、不 SSH、不讀 secret value。
"""

from __future__ import annotations

import argparse
import hashlib
import json
import subprocess
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any


TAIPEI = timezone(timedelta(hours=8))


SURFACES: list[dict[str, Any]] = [
    {
        "surface_id": "backup_all_orchestrator",
        "label": "全服務備份總控",
        "source_path": "scripts/backup/backup-all.sh",
        "expected_scope": "110_backup_host_all_services",
        "config_kind": "backup_orchestrator",
        "control_tier": "C0",
        "current_state": "write_capable_orchestrator_visible_not_executed",
        "backup_scope": ["gitea", "momo", "harbor", "awoooi", "langfuse", "monitoring", "signoz", "open-webui", "clawbot"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 backup owner、cron owner、失敗通知 owner、restore drill owner、rollback owner 與 post-check 指標。",
    },
    {
        "surface_id": "backup_common_restic_retention",
        "label": "Restic 共用設定與 GFS retention",
        "source_path": "scripts/backup/common.sh",
        "expected_scope": "restic_password_b2_retention_common",
        "config_kind": "backup_common_policy",
        "control_tier": "C0",
        "current_state": "retention_and_credential_metadata_visible_secret_values_absent",
        "backup_scope": ["RESTIC_PASSWORD_FILE", "B2 metadata", "KEEP_DAILY=30", "KEEP_WEEKLY=12", "KEEP_MONTHLY=24"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 restic password owner、B2 / rclone owner、retention owner、prune window 與 no-secret-value evidence。",
    },
    {
        "surface_id": "backup_gitea_service_script",
        "label": "Gitea 備份腳本",
        "source_path": "scripts/backup/backup-gitea.sh",
        "expected_scope": "gitea_database_and_repositories",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Gitea DB", "repositories", "app.ini redaction boundary"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Gitea backup owner、freshness evidence、restore target isolation 與 secret redaction proof。",
    },
    {
        "surface_id": "backup_momo_service_script",
        "label": "MOMO PostgreSQL 備份腳本",
        "source_path": "scripts/backup/backup-momo.sh",
        "expected_scope": "momo_postgresql",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["MOMO PostgreSQL", "188 database path"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 MOMO backup owner、188 DB access boundary、restore drill target 與 rollback owner。",
    },
    {
        "surface_id": "backup_harbor_service_script",
        "label": "Harbor 備份腳本",
        "source_path": "scripts/backup/backup-harbor.sh",
        "expected_scope": "harbor_registry_and_database",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Harbor registry", "Harbor DB", "image registry recovery"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Harbor backup owner、registry restore smoke、robot account secret boundary 與 image rollback owner。",
    },
    {
        "surface_id": "backup_awoooi_service_script",
        "label": "AWOOOI PostgreSQL 完整備份腳本",
        "source_path": "scripts/backup/backup-awoooi.sh",
        "expected_scope": "awoooi_postgresql_and_k3s_datastore",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["awoooi_prod", "awoooi_dev", "k3s datastore"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 AWOOOI DB backup owner、RPO owner、restore drill isolation 與 data masking policy。",
    },
    {
        "surface_id": "backup_awoooi_frequent_script",
        "label": "AWOOOI PostgreSQL 高頻備份腳本",
        "source_path": "scripts/backup/backup-awoooi-frequent.sh",
        "expected_scope": "awoooi_postgresql_high_frequency",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "high_frequency_backup_script_visible_gate_closed",
        "backup_scope": ["awoooi_prod", "6h RPO", "latest-only interaction"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補高頻備份 owner、cron owner、latest-only retention owner 與 freshness evidence。",
    },
    {
        "surface_id": "backup_langfuse_service_script",
        "label": "Langfuse 備份腳本",
        "source_path": "scripts/backup/backup-langfuse.sh",
        "expected_scope": "langfuse_ai_trace_database",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Langfuse DB", "AI trace evidence"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Langfuse backup owner、trace privacy boundary、restore smoke 與 secret redaction proof。",
    },
    {
        "surface_id": "backup_monitoring_service_script",
        "label": "Monitoring 備份腳本",
        "source_path": "scripts/backup/backup-monitoring.sh",
        "expected_scope": "prometheus_grafana_alertmanager",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Prometheus", "Grafana", "Alertmanager"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 observability backup owner、Grafana secret boundary、alert route restore smoke 與 rollback owner。",
    },
    {
        "surface_id": "backup_signoz_service_script",
        "label": "SigNoz 備份腳本",
        "source_path": "scripts/backup/backup-signoz.sh",
        "expected_scope": "signoz_clickhouse_and_sqlite",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["SigNoz ClickHouse", "SigNoz SQLite"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 SigNoz disruptive guard owner、ClickHouse restore owner、告警靜音邊界與 post-check 指標。",
    },
    {
        "surface_id": "backup_open_webui_service_script",
        "label": "Open-WebUI 備份腳本",
        "source_path": "scripts/backup/backup-open-webui.sh",
        "expected_scope": "open_webui_volume",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Open-WebUI volume", "LLM conversation data"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Open-WebUI data privacy owner、188 read boundary、restore target isolation 與 retention owner。",
    },
    {
        "surface_id": "backup_clawbot_service_script",
        "label": "ClawBot Redis 備份腳本",
        "source_path": "scripts/backup/backup-clawbot.sh",
        "expected_scope": "clawbot_redis_state",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["ClawBot Redis", "agent state cache"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 ClawBot state owner、Redis restore owner、agent state masking 與 rollback owner。",
    },
    {
        "surface_id": "backup_sentry_service_script",
        "label": "Sentry 備份腳本",
        "source_path": "scripts/backup/backup-sentry.sh",
        "expected_scope": "sentry_self_hosted",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "service_backup_script_visible_gate_closed",
        "backup_scope": ["Sentry", "ClickHouse / Postgres / Redis dependency boundary"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Sentry backup owner、multi-store restore owner、admin secret boundary 與 route smoke。",
    },
    {
        "surface_id": "backup_ai_artifacts_script",
        "label": "AI artifacts 備份腳本",
        "source_path": "scripts/backup/backup-ai-artifacts.sh",
        "expected_scope": "ai_artifacts",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "artifact_backup_script_visible_gate_closed",
        "backup_scope": ["AI artifacts", "model / evaluation outputs"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 artifact owner、retention owner、模型資料外送邊界與 restore validation。",
    },
    {
        "surface_id": "backup_public_routes_script",
        "label": "Public routes 備份腳本",
        "source_path": "scripts/backup/backup-public-routes.sh",
        "expected_scope": "public_route_reconstruction",
        "config_kind": "service_backup_script",
        "control_tier": "C0",
        "current_state": "route_backup_script_visible_gate_closed",
        "backup_scope": ["public routes", "Nginx route reconstruction", "frontend/API smoke evidence"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 route reconstruction owner、public/admin/API smoke、rollback ref 與 no-internal-transcript proof。",
    },
    {
        "surface_id": "config_backup_capture",
        "label": "Host / service / K8s 設定備份",
        "source_path": "scripts/backup/backup-configs.sh",
        "expected_scope": "110_188_120_121_cluster_configs",
        "config_kind": "config_backup_script",
        "control_tier": "C0",
        "current_state": "config_capture_visible_blocked_until_owner_evidence",
        "backup_scope": ["systemd", "docker", "nginx", "cron", "k8s", "host configs"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 config capture owner、secret redaction proof、120 blocked disposition、restore validation 與 retention owner。",
    },
    {
        "surface_id": "backup_status_reporter",
        "label": "備份狀態彙整腳本",
        "source_path": "scripts/backup/backup-status.sh",
        "expected_scope": "110_188_backup_status_summary",
        "config_kind": "backup_status_reporter",
        "control_tier": "C0",
        "current_state": "status_reporter_visible_not_executed",
        "backup_scope": ["freshness", "failure", "integrity", "restore drill", "offsite", "escrow"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 backup status owner、read-only execution window、SSH read boundary、notification owner 與 false-green 防線。",
    },
    {
        "surface_id": "backup_integrity_check",
        "label": "Restic integrity check",
        "source_path": "scripts/backup/check-backup-integrity.sh",
        "expected_scope": "restic_integrity_check",
        "config_kind": "integrity_check_script",
        "control_tier": "C0",
        "current_state": "integrity_check_visible_not_executed",
        "backup_scope": ["restic check", "read-data subset", "integrity evidence"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 integrity check owner、執行窗口、資源上限、結果證據與 restore drill 前置條件。",
    },
    {
        "surface_id": "latest_only_retention_enforcer",
        "label": "Latest-only retention enforcer",
        "source_path": "scripts/backup/enforce-latest-only-retention.sh",
        "expected_scope": "latest_only_retention",
        "config_kind": "retention_enforcer",
        "control_tier": "C0",
        "current_state": "delete_capable_retention_script_visible_gate_closed",
        "backup_scope": ["keep latest", "local delete", "retention marker"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 retention owner、刪除窗口、restore runway、offsite mirror interaction 與 rollback / stop condition。",
    },
    {
        "surface_id": "offsite_sync_controller",
        "label": "Offsite rclone sync controller",
        "source_path": "scripts/backup/sync-offsite-backups.sh",
        "expected_scope": "google_drive_rclone_offsite_mirror",
        "config_kind": "offsite_sync_controller",
        "control_tier": "C0",
        "current_state": "remote_write_and_delete_capable_sync_visible_gate_closed",
        "backup_scope": ["13 repos", "rclone sync", "remote delete", "success markers"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 offsite owner、remote delete owner、runway check、full sync window、rclone credential escrow 與 verifier evidence。",
    },
    {
        "surface_id": "offsite_full_sync_verifier",
        "label": "Offsite full sync verifier",
        "source_path": "scripts/backup/verify-offsite-full-sync.sh",
        "expected_scope": "offsite_full_sync_verification",
        "config_kind": "offsite_verifier",
        "control_tier": "C0",
        "current_state": "remote_read_and_textfile_write_capable_verifier_visible_gate_closed",
        "backup_scope": ["remote repo count", "latest-only evidence", "textfile metrics"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 verifier owner、remote read window、metric write owner、failure notification owner 與 evidence retention。",
    },
    {
        "surface_id": "offsite_readiness_gate",
        "label": "Offsite readiness gate",
        "source_path": "scripts/backup/backup-offsite-readiness-gate.sh",
        "expected_scope": "offsite_preflight_and_escrow_gate",
        "config_kind": "offsite_readiness_gate",
        "control_tier": "C0",
        "current_state": "readiness_gate_visible_not_executed",
        "backup_scope": ["status", "dry-run-small", "pre-full-sync", "escrow markers"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 readiness owner、dry-run scope、escrow owner、load/runway policy 與 accepted evidence refs。",
    },
    {
        "surface_id": "offsite_escrow_evidence_report",
        "label": "Offsite / escrow evidence report",
        "source_path": "scripts/backup/offsite-escrow-evidence-report.sh",
        "expected_scope": "offsite_escrow_redacted_report",
        "config_kind": "offsite_escrow_report",
        "control_tier": "C0",
        "current_state": "redacted_report_visible_default_no_remote_status",
        "backup_scope": ["script presence", "offsite marker", "escrow marker", "redacted output"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 evidence report owner、remote status opt-in owner、redaction proof 與 blocked marker disposition。",
    },
    {
        "surface_id": "credential_escrow_marker",
        "label": "Credential escrow marker writer",
        "source_path": "scripts/backup/mark-credential-escrow-verified.sh",
        "expected_scope": "credential_escrow_markers",
        "config_kind": "credential_escrow_marker",
        "control_tier": "C0",
        "current_state": "marker_write_capable_script_visible_gate_closed",
        "backup_scope": ["restic password", "offsite provider", "break-glass admin", "DNS recovery", "OAuth / AI provider recovery"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 escrow owner、non-secret evidence id、reviewer acceptance、marker write approval 與 retention owner。",
    },
    {
        "surface_id": "offsite_rclone_config",
        "label": "rclone offsite config helper",
        "source_path": "scripts/backup/configure-offsite-rclone.sh",
        "expected_scope": "rclone_config_metadata",
        "config_kind": "offsite_rclone_config",
        "control_tier": "C0",
        "current_state": "credential_config_helper_visible_secret_values_not_collected",
        "backup_scope": ["rclone remote", "Google Drive", "offsite.env metadata"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 rclone config owner、secret store owner、file mode evidence、no-value collection proof 與 recovery owner。",
    },
    {
        "surface_id": "offsite_b2_config",
        "label": "B2 offsite config helper",
        "source_path": "scripts/backup/configure-offsite-b2.sh",
        "expected_scope": "b2_config_metadata",
        "config_kind": "offsite_b2_config",
        "control_tier": "C0",
        "current_state": "credential_config_helper_visible_secret_values_not_collected",
        "backup_scope": ["Backblaze B2 metadata", "offsite env", "fallback provider"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 B2 provider owner、credential escrow owner、provider cost boundary 與 no-value collection proof。",
    },
    {
        "surface_id": "backup_health_textfile_exporter",
        "label": "Backup health textfile exporter",
        "source_path": "scripts/ops/backup-health-textfile-exporter.py",
        "expected_scope": "backup_health_prometheus_textfile",
        "config_kind": "backup_health_exporter",
        "control_tier": "C0",
        "current_state": "textfile_write_capable_exporter_visible_gate_closed",
        "backup_scope": ["freshness metrics", "restore drill metrics", "offsite metrics", "escrow metrics"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 exporter owner、textfile path owner、metric freshness SLO、false-green guard 與 alert owner。",
    },
    {
        "surface_id": "velero_restore_test_cronjob",
        "label": "Velero restore dry-run CronJob",
        "source_path": "k8s/awoooi-prod/16-cronjob-backup-restore-test.yaml",
        "expected_scope": "velero_weekly_restore_dry_run",
        "config_kind": "velero_restore_cronjob",
        "control_tier": "C0",
        "current_state": "k8s_cronjob_manifest_visible_not_applied_by_this_inventory",
        "backup_scope": ["Velero restore dry-run", "weekly schedule", "textfile metrics"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Velero owner、dry-run namespace isolation、CronJob live evidence、restore approval 與 post-check 指標。",
    },
    {
        "surface_id": "velero_restore_test_script_configmap",
        "label": "Velero restore script ConfigMap",
        "source_path": "k8s/awoooi-prod/17-configmap-backup-restore-scripts.yaml",
        "expected_scope": "velero_restore_script_configmap",
        "config_kind": "velero_restore_script_configmap",
        "control_tier": "C0",
        "current_state": "configmap_script_visible_timestamp_format_needs_owner_disposition",
        "backup_scope": ["restore dry-run script", "13-digit textfile timestamp risk", "Prometheus textfile"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 ConfigMap owner、timestamp format disposition、CronJob rollout owner、metric scrape proof 與 rollback owner。",
    },
    {
        "surface_id": "velero_standalone_restore_test_script",
        "label": "Velero restore dry-run standalone script",
        "source_path": "scripts/cron_backup_restore_test.sh",
        "expected_scope": "velero_standalone_restore_script",
        "config_kind": "velero_restore_standalone_script",
        "control_tier": "C0",
        "current_state": "standalone_script_visible_uses_seconds_textfile_timestamp_not_executed",
        "backup_scope": ["restore dry-run", "Prometheus textfile seconds timestamp", "failure metric"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 standalone / ConfigMap drift disposition、restore drill owner、textfile owner 與 proof of isolation。",
    },
    {
        "surface_id": "velero_credentials_manifest",
        "label": "Velero MinIO credential manifest",
        "source_path": "k8s/velero/01-credentials.yaml",
        "expected_scope": "velero_minio_credentials_metadata",
        "config_kind": "velero_credentials_manifest",
        "control_tier": "C0",
        "current_state": "placeholder_secret_manifest_visible_values_not_collected",
        "backup_scope": ["MinIO credential names", "placeholder values", "External Secrets / Sealed Secrets recommendation"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Velero credential owner、secret manager source、rotation owner、no-value collection proof 與 restore boundary。",
    },
    {
        "surface_id": "velero_install_manifest",
        "label": "Velero install manifest",
        "source_path": "k8s/velero/02-velero-install.yaml",
        "expected_scope": "velero_install_and_minio_storage",
        "config_kind": "velero_install_manifest",
        "control_tier": "C0",
        "current_state": "cluster_admin_velero_manifest_visible_gate_closed",
        "backup_scope": ["Velero Deployment", "cluster-admin binding", "MinIO s3Url", "backup storage location"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 Velero RBAC owner、MinIO endpoint owner、least privilege review、install window 與 rollback owner。",
    },
    {
        "surface_id": "backup_restore_alert_rules",
        "label": "Backup / restore alert rules",
        "source_path": "ops/monitoring/alerts.yml",
        "expected_scope": "backup_restore_prometheus_alerts",
        "config_kind": "backup_restore_alert_rules",
        "control_tier": "C0",
        "current_state": "alert_rule_source_visible_reload_not_authorized",
        "backup_scope": ["BackupRestoreTestFailed", "Velero freshness", "offsite freshness", "restore stale"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 alert rule owner、receiver owner、reload owner、silence boundary 與 failure-only notification policy。",
    },
    {
        "surface_id": "backup_dr_readiness_contract",
        "label": "Backup / DR readiness matrix",
        "source_path": "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json",
        "expected_scope": "backup_dr_readiness_contract",
        "config_kind": "dr_readiness_contract",
        "control_tier": "C0",
        "current_state": "readiness_contract_visible_action_required_items_not_accepted",
        "backup_scope": ["readiness matrix", "blocked targets", "restore drill status"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 readiness owner、blocked target disposition、freshness evidence、restore drill owner 與 accepted refs。",
    },
    {
        "surface_id": "backup_restore_drill_approval_template",
        "label": "Restore drill approval package template",
        "source_path": "docs/evaluations/backup_restore_drill_approval_package_template_2026-06-05.json",
        "expected_scope": "restore_drill_approval_template",
        "config_kind": "restore_drill_approval_template",
        "control_tier": "C0",
        "current_state": "approval_template_visible_no_restore_execution",
        "backup_scope": ["database restore", "configuration restore", "credential escrow", "K8s restore", "observability restore"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 owner response 實際封包、隔離環境、observer、rollback owner 與 restore stop condition。",
    },
    {
        "surface_id": "offsite_escrow_readiness_contract",
        "label": "Offsite / escrow readiness status",
        "source_path": "docs/evaluations/offsite_escrow_readiness_status_2026-06-05.json",
        "expected_scope": "offsite_escrow_readiness_contract",
        "config_kind": "offsite_escrow_readiness_contract",
        "control_tier": "C0",
        "current_state": "offsite_verified_but_escrow_and_velero_blocked",
        "backup_scope": ["offsite_rclone_full_sync", "credential_escrow_markers", "velero_k8s_resources"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 escrow marker owner、Velero metric binding、remote evidence expiry owner 與 offsite sync approval boundary。",
    },
    {
        "surface_id": "backup_status_runbook",
        "label": "Backup status runbook",
        "source_path": "docs/runbooks/BACKUP-STATUS.md",
        "expected_scope": "backup_status_runbook",
        "config_kind": "backup_status_runbook",
        "control_tier": "C0",
        "current_state": "runbook_visible_contains_live_refresh_notes_needs_revalidation",
        "backup_scope": ["110 backup center", "latest-only", "Google Drive / rclone", "credential escrow", "120 blocker"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補截至本次階段的 owner-provided live refresh、stale evidence disposition、escrow blocker owner 與 validation refs。",
    },
    {
        "surface_id": "cold_start_sop",
        "label": "Full-stack cold-start SOP",
        "source_path": "docs/runbooks/FULL-STACK-COLD-START-SOP.md",
        "expected_scope": "cold_start_backup_restore_recovery",
        "config_kind": "cold_start_sop",
        "control_tier": "C0",
        "current_state": "sop_visible_contains_backup_commands_not_authorized",
        "backup_scope": ["cold start", "backup-all", "sync-offsite", "restore guard", "schedules"],
        "requires_live_evidence": True,
        "requires_owner_response": True,
        "next_owner_action": "補 cold-start commander owner、backup command approval boundary、restore stop condition、rollback owner 與 post-start validation。",
    },
]


FALSE_BOUNDARIES = {
    "runtime_execution_authorized": False,
    "host_write_authorized": False,
    "backup_run_authorized": False,
    "restore_run_authorized": False,
    "restore_drill_authorized": False,
    "offsite_sync_authorized": False,
    "offsite_remote_delete_authorized": False,
    "credential_escrow_marker_write_authorized": False,
    "retention_change_authorized": False,
    "restic_prune_authorized": False,
    "rclone_config_authorized": False,
    "velero_restore_authorized": False,
    "velero_backup_authorized": False,
    "kubectl_action_authorized": False,
    "ssh_read_authorized": False,
    "ssh_write_authorized": False,
    "secret_value_collection_allowed": False,
    "active_scan_authorized": False,
    "action_buttons_allowed": False,
}


WRITE_CAPABLE_KINDS = {
    "backup_orchestrator",
    "service_backup_script",
    "config_backup_script",
    "retention_enforcer",
    "offsite_sync_controller",
    "offsite_verifier",
    "credential_escrow_marker",
    "offsite_rclone_config",
    "offsite_b2_config",
    "backup_health_exporter",
    "velero_restore_cronjob",
    "velero_restore_script_configmap",
    "velero_restore_standalone_script",
    "velero_credentials_manifest",
    "velero_install_manifest",
}

BACKUP_SCRIPT_KINDS = {
    "backup_orchestrator",
    "service_backup_script",
    "config_backup_script",
}

RESTORE_DRILL_KINDS = {
    "velero_restore_cronjob",
    "velero_restore_script_configmap",
    "velero_restore_standalone_script",
    "restore_drill_approval_template",
}

OFFSITE_ESCROW_KINDS = {
    "offsite_sync_controller",
    "offsite_verifier",
    "offsite_readiness_gate",
    "offsite_escrow_report",
    "credential_escrow_marker",
    "offsite_rclone_config",
    "offsite_b2_config",
    "offsite_escrow_readiness_contract",
}

VELERO_KINDS = {
    "velero_restore_cronjob",
    "velero_restore_script_configmap",
    "velero_restore_standalone_script",
    "velero_credentials_manifest",
    "velero_install_manifest",
}

RETENTION_SURFACE_IDS = {
    "backup_common_restic_retention",
    "latest_only_retention_enforcer",
    "offsite_sync_controller",
}

CREDENTIAL_SURFACE_IDS = {
    "backup_common_restic_retention",
    "credential_escrow_marker",
    "offsite_rclone_config",
    "offsite_b2_config",
    "velero_credentials_manifest",
}

DR_CONTRACT_KINDS = {
    "dr_readiness_contract",
    "restore_drill_approval_template",
    "offsite_escrow_readiness_contract",
}


def git_short_sha(root: Path) -> str:
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--short", "HEAD"],
            cwd=root,
            check=True,
            capture_output=True,
            text=True,
        )
        return result.stdout.strip()
    except Exception:
        return "unknown"


def file_metadata(root: Path, source_path: str) -> dict[str, Any]:
    path = root / source_path
    exists = path.exists()
    if not exists:
        return {"source_exists": False, "line_count": 0, "sha256": None}
    content = path.read_bytes()
    return {
        "source_exists": True,
        "line_count": len(content.decode("utf-8", errors="replace").splitlines()),
        "sha256": hashlib.sha256(content).hexdigest(),
    }


def build_surface(root: Path, surface: dict[str, Any]) -> dict[str, Any]:
    metadata = file_metadata(root, surface["source_path"])
    return {
        **surface,
        **metadata,
        "owner_response_received": False,
        "owner_response_accepted": False,
        "live_evidence_received": False,
        "restore_drill_accepted": False,
        "offsite_sync_accepted": False,
        "credential_escrow_accepted": False,
        "retention_change_accepted": False,
        "maintenance_window_accepted": False,
        "rollback_owner_accepted": False,
        "runtime_gate_open": False,
        "action_buttons_allowed": False,
    }


def count_kind(surfaces: list[dict[str, Any]], kinds: set[str]) -> int:
    return sum(1 for surface in surfaces if surface["config_kind"] in kinds)


def build_report(root: Path, generated_at: str | None) -> dict[str, Any]:
    report_time = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds")
    surfaces = [build_surface(root, surface) for surface in SURFACES]
    expected_scopes = sorted({surface["expected_scope"] for surface in surfaces})
    write_capable = [surface for surface in surfaces if surface["config_kind"] in WRITE_CAPABLE_KINDS]

    return {
        "schema_version": "backup_restore_escrow_inventory_v1",
        "generated_at": report_time,
        "git_commit": git_short_sha(root),
        "status": "repo_only_inventory_ready",
        "source_scope": "committed_repo_files_only",
        "summary": {
            "surface_count": len(surfaces),
            "source_exists_count": sum(1 for surface in surfaces if surface["source_exists"]),
            "expected_scope_count": len(expected_scopes),
            "backup_script_surface_count": count_kind(surfaces, BACKUP_SCRIPT_KINDS),
            "restore_drill_surface_count": count_kind(surfaces, RESTORE_DRILL_KINDS),
            "offsite_escrow_surface_count": count_kind(surfaces, OFFSITE_ESCROW_KINDS),
            "velero_surface_count": count_kind(surfaces, VELERO_KINDS),
            "retention_surface_count": sum(1 for surface in surfaces if surface["surface_id"] in RETENTION_SURFACE_IDS),
            "credential_surface_count": sum(1 for surface in surfaces if surface["surface_id"] in CREDENTIAL_SURFACE_IDS),
            "alert_surface_count": sum(1 for surface in surfaces if surface["config_kind"] == "backup_restore_alert_rules"),
            "dr_readiness_contract_surface_count": count_kind(surfaces, DR_CONTRACT_KINDS),
            "write_capable_surface_count": len(write_capable),
            "surfaces_requiring_owner_response_count": sum(1 for surface in surfaces if surface["requires_owner_response"]),
            "surfaces_requiring_live_evidence_count": sum(1 for surface in surfaces if surface["requires_live_evidence"]),
            "owner_response_received_count": 0,
            "owner_response_accepted_count": 0,
            "live_evidence_received_count": 0,
            "restore_drill_accepted_count": 0,
            "offsite_sync_accepted_count": 0,
            "credential_escrow_accepted_count": 0,
            "retention_change_accepted_count": 0,
            "maintenance_window_accepted_count": 0,
            "rollback_owner_accepted_count": 0,
            "runtime_gate_count": 0,
            "action_button_count": 0,
            "coverage_percent_before_inventory": 52,
            "coverage_percent_after_inventory": 58,
        },
        "execution_boundaries": FALSE_BOUNDARIES,
        "expected_scopes": expected_scopes,
        "backup_surfaces": surfaces,
        "write_capable_surfaces": [
            {
                "surface_id": surface["surface_id"],
                "label": surface["label"],
                "config_kind": surface["config_kind"],
                "expected_scope": surface["expected_scope"],
                "required_gate": "owner_response_plus_maintenance_window_plus_rollback_owner",
            }
            for surface in write_capable
        ],
        "next_collection_order": [
            "backup_common_restic_retention",
            "offsite_sync_controller",
            "credential_escrow_marker",
            "velero_restore_test_script_configmap",
            "velero_credentials_manifest",
            "backup_health_textfile_exporter",
            "backup_restore_alert_rules",
            "backup_restore_drill_approval_template",
            "backup_status_runbook",
            "cold_start_sop",
        ],
        "operator_interpretation": [
            "這是 repo-only backup / restore / escrow / retention 清冊，不是 live backup、remote provider 或 cluster truth。",
            "source_exists=true 只代表 repo 檔案存在；不代表備份已成功、restore drill 已執行、offsite sync 已授權或 escrow marker 已可寫入。",
            "write-capable surface 可見代表需要資安控管，不代表 backup、restore、rclone sync、remote delete、restic prune、Velero restore 或 kubectl 已授權。",
            "所有 owner response、live evidence、restore drill acceptance、offsite sync acceptance、credential escrow acceptance、retention change acceptance 與 runtime gate 仍為 0。",
        ],
    }


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="產生 IwoooS backup / restore / escrow / retention repo-only 清冊")
    parser.add_argument("--root", default=".", help="repo root")
    parser.add_argument("--output", help="輸出 JSON 檔")
    parser.add_argument("--generated-at", help="固定 generated_at")
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    root = Path(args.root).resolve()
    report = build_report(root, args.generated_at)
    text = json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)
    if args.output:
        Path(args.output).write_text(text + "\n", encoding="utf-8")
    else:
        print(text)
    print(
        "BACKUP_RESTORE_ESCROW_INVENTORY_OK "
        f"surfaces={report['summary']['surface_count']} "
        f"backup_scripts={report['summary']['backup_script_surface_count']} "
        f"offsite_escrow={report['summary']['offsite_escrow_surface_count']} "
        f"runtime_gate={report['summary']['runtime_gate_count']}",
        file=sys.stderr,
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
