149 lines
4.6 KiB
Python
149 lines
4.6 KiB
Python
"""Bridge governance agents into the unified Agent Event Bus.
|
|
|
|
This module emits normalized governance events for requirement changes,
|
|
architecture reviews, user acceptance, test gates and version decisions. It uses
|
|
duck typing so it can work with the unified AgentEventBus when available, while
|
|
remaining testable without a running bus.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass, field
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Protocol
|
|
from uuid import uuid4
|
|
|
|
|
|
def utc_now() -> str:
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
class EventPublisher(Protocol):
|
|
def publish(
|
|
self,
|
|
kind: str,
|
|
source: str,
|
|
target: str,
|
|
summary: str,
|
|
payload: dict[str, Any],
|
|
correlation_id: str | None = None,
|
|
) -> dict[str, Any]:
|
|
...
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class GovernanceEventEnvelope:
|
|
id: str
|
|
kind: str
|
|
source: str
|
|
target: str
|
|
summary: str
|
|
payload: dict[str, Any]
|
|
correlation_id: str | None = None
|
|
created_at: str = field(default_factory=utc_now)
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
class InMemoryGovernanceEventBus:
|
|
"""Small fallback bus for tests and offline governance runs."""
|
|
|
|
def __init__(self) -> None:
|
|
self.events: list[dict[str, Any]] = []
|
|
|
|
def publish(
|
|
self,
|
|
kind: str,
|
|
source: str,
|
|
target: str,
|
|
summary: str,
|
|
payload: dict[str, Any],
|
|
correlation_id: str | None = None,
|
|
) -> dict[str, Any]:
|
|
event = GovernanceEventEnvelope(
|
|
id=f"evt_{uuid4().hex[:12]}",
|
|
kind=kind,
|
|
source=source,
|
|
target=target,
|
|
summary=summary,
|
|
payload=payload,
|
|
correlation_id=correlation_id,
|
|
).to_dict()
|
|
self.events.append(event)
|
|
return event
|
|
|
|
|
|
class GovernanceEventBridge:
|
|
"""Publishes governance lifecycle events to the Agent Event Bus."""
|
|
|
|
def __init__(self, bus: EventPublisher | None = None):
|
|
self.bus = bus or InMemoryGovernanceEventBus()
|
|
|
|
def requirement_changed(self, requirement: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="requirement_changed",
|
|
source=requirement.get("source", "unknown"),
|
|
target="architect-agent",
|
|
summary=requirement.get("requirement", "requirement changed"),
|
|
payload=requirement,
|
|
correlation_id=requirement.get("id"),
|
|
)
|
|
|
|
def architecture_review_started(self, proposal: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="architecture_review_started",
|
|
source="architect-agent",
|
|
target="architecture-review-agent",
|
|
summary=proposal.get("title", "architecture review started"),
|
|
payload=proposal,
|
|
correlation_id=proposal.get("change_id"),
|
|
)
|
|
|
|
def architecture_review_completed(self, decision: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="architecture_review_completed",
|
|
source="architecture-review-agent",
|
|
target="change-governance",
|
|
summary=f"architecture review {decision.get('status', 'completed')}",
|
|
payload=decision,
|
|
correlation_id=decision.get("change_id"),
|
|
)
|
|
|
|
def user_acceptance_completed(self, report: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="user_acceptance_completed",
|
|
source="user-acceptance-agent",
|
|
target="change-governance",
|
|
summary=f"user acceptance {report.get('decision', 'completed')}",
|
|
payload=report,
|
|
correlation_id=report.get("change_id"),
|
|
)
|
|
|
|
def test_gate_completed(self, result: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="test_gate_completed",
|
|
source="test-gate",
|
|
target="change-governance",
|
|
summary=f"test gate {result.get('status', 'completed')}",
|
|
payload=result,
|
|
correlation_id=result.get("change_id"),
|
|
)
|
|
|
|
def version_decision_created(self, decision: dict[str, Any]) -> dict[str, Any]:
|
|
return self.bus.publish(
|
|
kind="version_decision_created",
|
|
source="architect-agent",
|
|
target="owner-gate",
|
|
summary=f"version bump recommendation: {decision.get('version_bump', 'unknown')}",
|
|
payload=decision,
|
|
correlation_id=decision.get("change_id"),
|
|
)
|
|
|
|
|
|
__all__ = [
|
|
"GovernanceEventEnvelope",
|
|
"InMemoryGovernanceEventBus",
|
|
"GovernanceEventBridge",
|
|
]
|