feat(governance): 新增服務健康失敗限定通知合約
Some checks failed
CD Pipeline / tests (push) Successful in 1m34s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-05 15:33:41 +08:00
parent fd88ade112
commit 5c2ac4d502
17 changed files with 1746 additions and 70 deletions

View File

@@ -74,6 +74,9 @@ from src.services.ai_provider_route_matrix import (
from src.services.service_health_gap_matrix import (
load_latest_service_health_gap_matrix,
)
from src.services.service_health_failure_notification_policy import (
load_latest_service_health_failure_notification_policy,
)
from src.services.package_supply_chain_inventory import (
load_latest_package_supply_chain_inventory,
)
@@ -709,6 +712,34 @@ async def get_backup_notification_policy() -> dict[str, Any]:
) from exc
@router.get(
"/service-health-failure-notification-policy",
response_model=dict[str, Any],
summary="取得服務健康失敗限定通知合約",
description=(
"讀取最新已提交的 service health failure-only Telegram / AwoooP 通知合約;"
"此端點只回傳成功降噪、action-required 與 failure escalation 規則,"
"不送通知、不做 live probe、不重啟服務、不改 endpoint、不觸發 workflow / runtime execution、"
"不讀取 secret payload、不回傳工作視窗對話內容或 prompt。"
),
)
async def get_service_health_failure_notification_policy() -> dict[str, Any]:
"""Return the latest read-only service health failure-only notification policy."""
try:
return await asyncio.to_thread(load_latest_service_health_failure_notification_policy)
except FileNotFoundError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(exc),
) from exc
except (json.JSONDecodeError, ValueError) as exc:
logger.error("service_health_failure_notification_policy_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="服務健康失敗限定通知合約快照無效",
) from exc
@router.get(
"/backup-restore-drill-approval-package-template",
response_model=dict[str, Any],

View File

@@ -0,0 +1,269 @@
"""
Service health failure-only notification policy snapshot.
Loads the latest committed, read-only service health notification policy. The
policy defines success-noise suppression, failure/action-required escalation,
message redaction, and frontend display limits. It never sends Telegram or
AwoooP notifications, writes operator events, probes live systems, restarts
services, changes endpoints, triggers workflows, reads secrets, or displays
work-window conversation transcripts.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from src.services.snapshot_paths import default_evaluations_dir
_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__))
_SNAPSHOT_PATTERN = "service_health_failure_notification_policy_*.json"
_SCHEMA_VERSION = "service_health_failure_notification_policy_v1"
_CONVERSATION_TRANSCRIPT_MARKERS = {
"# In app browser",
"My request for Codex",
"Current URL:",
"AGENTS.md instructions",
"<environment_context>",
"批准!繼續",
}
def load_latest_service_health_failure_notification_policy(
evaluations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load the newest committed service health failure notification policy."""
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
if not candidates:
raise FileNotFoundError(
f"no service health failure notification policy snapshots found in {directory}"
)
latest = candidates[-1]
with latest.open(encoding="utf-8") as handle:
payload = json.load(handle)
if not isinstance(payload, dict):
raise ValueError(f"{latest}: expected JSON object")
_require_schema(payload, _SCHEMA_VERSION, str(latest))
_require_read_only_boundaries(payload, str(latest))
_require_operation_boundaries(payload, str(latest))
_require_rollup_consistency(payload, str(latest))
_require_success_noise_suppression(payload, str(latest))
_require_failure_only_escalation(payload, str(latest))
_require_frontend_redaction_contract(payload, str(latest))
_require_no_plaintext_secret_payload_keys(payload, str(latest))
_require_no_conversation_transcript_content(payload, str(latest))
return payload
def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None:
actual = payload.get("schema_version")
if actual != expected:
raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}")
def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None:
program_status = payload.get("program_status") or {}
if program_status.get("read_only_mode") is not True:
raise ValueError(f"{label}: program_status.read_only_mode must be true")
approval_boundaries = payload.get("approval_boundaries") or {}
allowed = sorted(key for key, value in approval_boundaries.items() if value is not False)
if allowed:
raise ValueError(f"{label}: approval boundaries must remain false: {allowed}")
def _require_operation_boundaries(payload: dict[str, Any], label: str) -> None:
boundaries = payload.get("operation_boundaries") or {}
if boundaries.get("read_only_policy_allowed") is not True:
raise ValueError(f"{label}: read_only_policy_allowed must be true")
blocked_flags = {
"notification_send_allowed",
"telegram_test_message_allowed",
"awooop_event_write_allowed",
"live_probe_allowed",
"external_health_probe_allowed",
"service_restart_allowed",
"endpoint_change_allowed",
"workflow_trigger_allowed",
"runtime_execution_allowed",
"secret_plaintext_allowed",
}
allowed = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False)
if allowed:
raise ValueError(f"{label}: operation boundaries must remain false: {allowed}")
def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None:
rules = payload.get("policy_rules") or []
rollups = payload.get("rollups") or {}
if rollups.get("total_rules") != len(rules):
raise ValueError(f"{label}: rollups.total_rules must match policy_rules")
by_decision: dict[str, int] = {}
for rule in rules:
decision = str(rule.get("decision"))
by_decision[decision] = by_decision.get(decision, 0) + 1
if rollups.get("by_decision") != by_decision:
raise ValueError(f"{label}: rollups.by_decision must match policy rule decisions")
immediate_ids = {
rule.get("rule_id")
for rule in rules
if rule.get("decision") == "escalate_immediate"
}
if set(rollups.get("immediate_escalation_rule_ids") or []) != immediate_ids:
raise ValueError(f"{label}: rollups.immediate_escalation_rule_ids must match immediate rules")
suppressed_success_ids = {
rule.get("rule_id")
for rule in rules
if rule.get("service_state") == "verified"
and rule.get("decision") == "suppress_immediate_success"
}
if set(rollups.get("suppressed_success_rule_ids") or []) != suppressed_success_ids:
raise ValueError(f"{label}: rollups.suppressed_success_rule_ids must match suppressed success rules")
action_required_ids = {
rule.get("rule_id")
for rule in rules
if rule.get("decision") == "create_action_required"
}
if set(rollups.get("action_required_rule_ids") or []) != action_required_ids:
raise ValueError(f"{label}: rollups.action_required_rule_ids must match action-required rules")
if rollups.get("notification_send_allowed_count") != 0:
raise ValueError(f"{label}: rollups.notification_send_allowed_count must remain 0")
def _require_success_noise_suppression(payload: dict[str, Any], label: str) -> None:
channels = payload.get("notification_channels") or []
noisy_channels = [
channel.get("channel_id")
for channel in channels
if channel.get("success_immediate_allowed") is not False
]
if noisy_channels:
raise ValueError(f"{label}: channels must not allow success immediate notifications: {noisy_channels}")
success_escalations = [
rule.get("rule_id")
for rule in payload.get("policy_rules") or []
if rule.get("service_state") == "verified"
and rule.get("decision") != "suppress_immediate_success"
]
if success_escalations:
raise ValueError(f"{label}: verified service health rules must suppress immediate notifications")
template_contract = payload.get("message_template_contract") or {}
success_policy = str(template_contract.get("success_message_policy") or "")
if "不得即時" not in success_policy or "Telegram / AwoooP" not in success_policy:
raise ValueError(f"{label}: success_message_policy must suppress Telegram / AwoooP success noise")
def _require_failure_only_escalation(payload: dict[str, Any], label: str) -> None:
escalation_rules = [
rule
for rule in payload.get("policy_rules") or []
if rule.get("decision") == "escalate_immediate"
]
invalid_states = sorted(
rule.get("rule_id")
for rule in escalation_rules
if rule.get("service_state") not in {"failed", "blocked"}
)
if invalid_states:
raise ValueError(f"{label}: immediate escalation must be failure-only: {invalid_states}")
telegram_non_failure = sorted(
rule.get("rule_id")
for rule in payload.get("policy_rules") or []
if "telegram_ops" in (rule.get("channels") or [])
and rule.get("decision") != "escalate_immediate"
)
if telegram_non_failure:
raise ValueError(f"{label}: telegram_ops must only appear on immediate failure escalation rules")
template_contract = payload.get("message_template_contract") or {}
required_fields = set(template_contract.get("required_fields") or [])
required = {"stage", "next_action", "blocked_reason", "target_id", "severity", "evidence_ref"}
if not required.issubset(required_fields):
raise ValueError(f"{label}: message_template_contract.required_fields missing failure context")
forbidden_fields = set(template_contract.get("forbidden_fields") or [])
required_forbidden_fields = {
"secret_value",
"token",
"authorization_header",
"work_window_transcript",
"codex_user_message",
"prompt_text",
"chain_of_thought",
"session_id",
"browser_context",
}
if not required_forbidden_fields.issubset(forbidden_fields):
raise ValueError(f"{label}: message_template_contract.forbidden_fields missing redaction boundary")
def _require_frontend_redaction_contract(payload: dict[str, Any], label: str) -> None:
contract = payload.get("display_redaction_contract") or {}
if contract.get("conversation_transcript_display_allowed") is not False:
raise ValueError(f"{label}: conversation transcript display must remain false")
if contract.get("redaction_required") is not True:
raise ValueError(f"{label}: frontend redaction must be required")
forbidden = set(contract.get("forbidden_frontend_content") or [])
required_forbidden = {
"工作視窗對話內容",
"Codex / user 訊息逐字稿",
"prompt / chain-of-thought",
"session id / browser context",
"secret / token / authorization header",
}
if not required_forbidden.issubset(forbidden):
raise ValueError(f"{label}: display_redaction_contract is missing required forbidden content")
allowed_fields = set(contract.get("allowed_frontend_fields") or [])
if "committed evidence ref" not in allowed_fields or "policy rule summary" not in allowed_fields:
raise ValueError(f"{label}: display_redaction_contract must limit frontend to committed policy evidence")
def _require_no_plaintext_secret_payload_keys(value: Any, label: str, path: str = "$") -> None:
if isinstance(value, dict):
forbidden_key_fragments = {
"secret_value",
"token_value",
"authorization_header",
"private_key",
"webhook_secret",
"runner_token",
"api_key_value",
"password_value",
}
for key, nested in value.items():
lowered = str(key).lower()
if any(fragment in lowered for fragment in forbidden_key_fragments):
raise ValueError(f"{label}: forbidden secret payload key at {path}.{key}")
_require_no_plaintext_secret_payload_keys(nested, label, f"{path}.{key}")
elif isinstance(value, list):
for index, nested in enumerate(value):
_require_no_plaintext_secret_payload_keys(nested, label, f"{path}[{index}]")
def _require_no_conversation_transcript_content(value: Any, label: str, path: str = "$") -> None:
if isinstance(value, dict):
for key, nested in value.items():
_require_no_conversation_transcript_content(nested, label, f"{path}.{key}")
elif isinstance(value, list):
for index, nested in enumerate(value):
_require_no_conversation_transcript_content(nested, label, f"{path}[{index}]")
elif isinstance(value, str):
for marker in _CONVERSATION_TRANSCRIPT_MARKERS:
if marker in value:
raise ValueError(f"{label}: forbidden work-window conversation content at {path}")