91 lines
3.2 KiB
Python
91 lines
3.2 KiB
Python
"""Architecture change classification.
|
|
|
|
Small compatible architecture changes may be handled automatically by the
|
|
Architect Agent with audit evidence. Large, risky, production-impacting or
|
|
breaking architecture changes require explicit Owner approval before
|
|
implementation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass, field
|
|
from typing import Any
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ArchitectureChangeClassification:
|
|
level: str
|
|
owner_required: bool
|
|
auto_implement_allowed: bool
|
|
version_bump: str
|
|
reasons: list[str] = field(default_factory=list)
|
|
required_gates: list[str] = field(default_factory=list)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
class ArchitectureChangeClassifier:
|
|
"""Classifies architecture changes into small/medium/large governance paths."""
|
|
|
|
LARGE_FLAGS = {
|
|
"breaking_change",
|
|
"breaks_architecture",
|
|
"production_architecture_update",
|
|
"security_boundary_change",
|
|
"cross_tenant_effect",
|
|
"cross_user_data_effect",
|
|
"data_migration",
|
|
"permission_model_change",
|
|
"runtime_infrastructure_change",
|
|
}
|
|
|
|
def classify(self, change: dict[str, Any]) -> ArchitectureChangeClassification:
|
|
risk = str(change.get("risk", "medium")).lower()
|
|
version_bump = str(change.get("version_bump", change.get("recommended_version_bump", "C"))).upper()
|
|
scope = str(change.get("scope", "component")).lower()
|
|
reasons: list[str] = []
|
|
|
|
for flag in sorted(self.LARGE_FLAGS):
|
|
if change.get(flag):
|
|
reasons.append(flag)
|
|
|
|
if risk in {"high", "critical"}:
|
|
reasons.append(f"risk={risk}")
|
|
if version_bump == "V":
|
|
reasons.append("version_bump=V")
|
|
if version_bump == "R" and scope in {"system", "platform", "production", "cross_agent", "cross_tenant"}:
|
|
reasons.append(f"version_bump=R scope={scope}")
|
|
|
|
if reasons:
|
|
return ArchitectureChangeClassification(
|
|
level="large",
|
|
owner_required=True,
|
|
auto_implement_allowed=False,
|
|
version_bump=version_bump,
|
|
reasons=reasons,
|
|
required_gates=["architect_review", "owner_architecture_approval", "test_gate", "promotion_gate"],
|
|
)
|
|
|
|
if risk == "low" and version_bump in {"C", "NONE"} and scope in {"component", "local", "documentation", "test"}:
|
|
return ArchitectureChangeClassification(
|
|
level="small",
|
|
owner_required=False,
|
|
auto_implement_allowed=True,
|
|
version_bump=version_bump,
|
|
reasons=["low_risk_compatible_change"],
|
|
required_gates=["architect_review", "audit", "test_gate"],
|
|
)
|
|
|
|
return ArchitectureChangeClassification(
|
|
level="medium",
|
|
owner_required=False,
|
|
auto_implement_allowed=False,
|
|
version_bump=version_bump,
|
|
reasons=["requires_architect_review_before_implementation"],
|
|
required_gates=["architect_review", "test_gate", "audit"],
|
|
)
|
|
|
|
|
|
__all__ = ["ArchitectureChangeClassification", "ArchitectureChangeClassifier"]
|