"""Architecture change governance dashboard. Displays architecture change classification, owner approval requirements, implementation readiness, version recommendation and audit evidence for Web UI. """ from __future__ import annotations from dataclasses import asdict, dataclass, field from datetime import datetime, timezone from typing import Any from .architecture_change_classifier import ArchitectureChangeClassifier def utc_now() -> str: return datetime.now(timezone.utc).isoformat() @dataclass(frozen=True) class ArchitectureChangeView: change_id: str title: str level: str owner_required: bool auto_implement_allowed: bool version_bump: str status: str reasons: list[str] = field(default_factory=list) required_gates: list[str] = field(default_factory=list) owner_decision: str | None = None def to_dict(self) -> dict[str, Any]: return asdict(self) class ArchitectureChangeDashboard: def __init__(self, classifier: ArchitectureChangeClassifier | None = None): self.classifier = classifier or ArchitectureChangeClassifier() def overview(self, changes: list[dict[str, Any]] | None = None) -> dict[str, Any]: changes = changes or [] views = [self._view(change) for change in changes] return { "module": "architecture-change-governance", "route": "/admin/ai-evolution/architecture-changes", "title": "架构变更治理中心", "status": self._status(views), "generated_at": utc_now(), "summary": self._summary(views), "changes": [view.to_dict() for view in views], } def _view(self, change: dict[str, Any]) -> ArchitectureChangeView: classification = self.classifier.classify(change) owner_decision = change.get("owner_decision") if classification.owner_required and owner_decision != "approved": status = "waiting_owner_approval" elif classification.auto_implement_allowed: status = "auto_implementation_allowed" else: status = "ready_after_required_gates" return ArchitectureChangeView( change_id=change.get("change_id", change.get("id", "unknown")), title=change.get("title", "architecture change"), level=classification.level, owner_required=classification.owner_required, auto_implement_allowed=classification.auto_implement_allowed, version_bump=classification.version_bump, status=status, reasons=classification.reasons, required_gates=classification.required_gates, owner_decision=owner_decision, ) def _status(self, views: list[ArchitectureChangeView]) -> str: if any(view.status == "waiting_owner_approval" for view in views): return "attention_required" return "healthy" def _summary(self, views: list[ArchitectureChangeView]) -> dict[str, Any]: return { "total": len(views), "small": len([v for v in views if v.level == "small"]), "medium": len([v for v in views if v.level == "medium"]), "large": len([v for v in views if v.level == "large"]), "waiting_owner_approval": len([v for v in views if v.status == "waiting_owner_approval"]), "auto_allowed": len([v for v in views if v.auto_implement_allowed]), "version_bumps": sorted({v.version_bump for v in views if v.version_bump and v.version_bump != "NONE"}), } __all__ = ["ArchitectureChangeView", "ArchitectureChangeDashboard"]