90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
"""Owner approval gate for architecture changes.
|
|
|
|
Architecture changes may be proposed, reviewed and optimized by agents, but they
|
|
must not be implemented until owner approval is recorded. This gate sits before
|
|
implementation and before promotion.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass, field
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
|
|
|
|
def utc_now() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ArchitectureOwnerApprovalRequest:
|
|
request_id: str
|
|
change_id: str
|
|
title: str
|
|
reason: str
|
|
risk: str
|
|
architect_decision: str
|
|
status: str = "pending_owner_approval"
|
|
created_at: str = field(default_factory=utc_now)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ArchitectureOwnerDecision:
|
|
request_id: str
|
|
change_id: str
|
|
decision: str
|
|
decided_by: str
|
|
reason: str = ""
|
|
decided_at: str = field(default_factory=utc_now)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
class ArchitectureOwnerGate:
|
|
"""Blocks architecture implementation until owner explicitly approves."""
|
|
|
|
def create_request(self, change: dict[str, Any], architect_decision: dict[str, Any]) -> ArchitectureOwnerApprovalRequest:
|
|
return ArchitectureOwnerApprovalRequest(
|
|
request_id=f"ARCH-OWNER-{change.get('change_id', change.get('id', 'unknown'))}",
|
|
change_id=change.get("change_id", change.get("id", "unknown")),
|
|
title=change.get("title", "architecture change"),
|
|
reason=change.get("reason", "architecture impact requires owner approval"),
|
|
risk=change.get("risk", "medium"),
|
|
architect_decision=architect_decision.get("status", "reviewed"),
|
|
)
|
|
|
|
def can_implement(self, request: dict[str, Any], decision: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
if not decision:
|
|
return {
|
|
"allowed": False,
|
|
"status": "waiting_owner_approval",
|
|
"reason": "architecture change requires owner approval before implementation",
|
|
"request": request,
|
|
}
|
|
if decision.get("decision") == "approved":
|
|
return {
|
|
"allowed": True,
|
|
"status": "owner_approved",
|
|
"reason": decision.get("reason", "approved by owner"),
|
|
"request": request,
|
|
"decision": decision,
|
|
}
|
|
return {
|
|
"allowed": False,
|
|
"status": "owner_rejected",
|
|
"reason": decision.get("reason", "rejected by owner"),
|
|
"request": request,
|
|
"decision": decision,
|
|
}
|
|
|
|
|
|
__all__ = [
|
|
"ArchitectureOwnerApprovalRequest",
|
|
"ArchitectureOwnerDecision",
|
|
"ArchitectureOwnerGate",
|
|
]
|