#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ARTIFACT_SUMMARY_PATH="${ARTIFACT_SUMMARY_PATH:-$ROOT_DIR/artifacts/v3-governance-live/20260608_102323/99-summary.json}" fail() { echo "FAIL: $*" >&2 exit 1 } log() { echo "==> $*" } require_file() { local path="$1" [[ -f "$path" ]] || fail "missing required file: $path" } require_contains() { local path="$1" local needle="$2" grep -F "$needle" "$path" >/dev/null || fail "missing expected text in $path: $needle" } log "checking V3-2 source-of-truth files" require_file "$ROOT_DIR/docs/2026-06-04-SLO_AND_OBSERVABILITY.md" require_file "$ROOT_DIR/docs/2026-06-04-KEY_ACCOUNT_GOVERNANCE.md" require_file "$ROOT_DIR/docs/EXECUTION_BOARD.md" require_file "$ROOT_DIR/deploy/monitoring/prometheus-rules.yml" require_file "$ARTIFACT_SUMMARY_PATH" log "checking metrics wiring truth" require_contains "$ROOT_DIR/internal/metrics/metrics.go" 'Name: "user_key_operations_total"' require_contains "$ROOT_DIR/internal/metrics/metrics.go" 'Name: "user_key_chat_requests_total"' require_contains "$ROOT_DIR/internal/metrics/metrics.go" 'statusLabel := strconv.Itoa(status)' require_contains "$ROOT_DIR/internal/app/route_resolve_api.go" 'decisionStatus = "failover"' require_contains "$ROOT_DIR/internal/app/key_self_service_svc.go" 'recordUserKeyFailure("create", "resolve_host_error"' require_contains "$ROOT_DIR/internal/app/key_self_service_svc.go" 'recordUserKeyFailure("delete", "get_key_error"' log "checking alert rule alignment" require_contains "$ROOT_DIR/deploy/monitoring/prometheus-rules.yml" 'user_key_chat_requests_total{result="ok"}' require_contains "$ROOT_DIR/deploy/monitoring/prometheus-rules.yml" 'user_key_operations_total{operation="create",result!~"success|rate_limited"}' require_contains "$ROOT_DIR/deploy/monitoring/prometheus-rules.yml" 'route_decisions_total{status="failover"}' require_contains "$ROOT_DIR/deploy/monitoring/prometheus-rules.yml" 'http_requests_total{status=~"4..|5.."}' log "checking live governance artifact" python3 - "$ARTIFACT_SUMMARY_PATH" <<'PY' import json, sys from pathlib import Path p = Path(sys.argv[1]) obj = json.loads(p.read_text()) checks = { 'create_http': 201, 'chat_before_http': 200, 'pause_http': 200, 'get_paused_http': 200, 'chat_paused_http': 403, 'resume_http': 200, 'get_resumed_http': 200, 'chat_resumed_http': 200, 'delete_http': 200, } for key, want in checks.items(): got = obj.get(key) if got != want: raise SystemExit(f'{key}={got!r}, want {want!r}') paused_body = obj.get('chat_paused_body', '') if 'key_paused' not in paused_body: raise SystemExit('chat_paused_body missing key_paused evidence') print(json.dumps({'artifact': str(p), 'checks': checks, 'paused_error': 'key_paused'}, ensure_ascii=False, indent=2)) PY log "checking docs mention V3-2 closure state" require_contains "$ROOT_DIR/docs/EXECUTION_BOARD.md" 'V3-2 SLO / 观测最小闭环(2026-06-08 首批)' require_contains "$ROOT_DIR/docs/EXECUTION_BOARD.md" '失败路径细化、告警规则、发布门禁均已落地' echo 'PASS: V3-2 SLO release gate verified'