mirror of
https://github.com/mcloh/Enterprise-MCP-Service-Bus.git
synced 2026-11-06 06:24:12 +00:00
M0 e M1 revisados e fixes implementados
This commit is contained in:
83
RI/src/emcp_bus/registry/pipeline.py
Normal file
83
RI/src/emcp_bus/registry/pipeline.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""Capability publishing pipeline (EP-02-T02, README.md §6.8).
|
||||
|
||||
Manifest -> [schema | risk | policy | backend gates] -> Gate -> Registry
|
||||
|
||||
Fails closed: any gate rejects the *whole* manifest, nothing partially
|
||||
enters the Registry -- there is no ad hoc manual registration path (RF-11).
|
||||
Schema/naming (name format, required fields) is already enforced by
|
||||
`CapabilityManifest` itself at construction (EP-02-T01); the gates here are
|
||||
the business rules a merely well-typed manifest can still violate.
|
||||
|
||||
A network health check of `backend.service` is a natural extension (README.md
|
||||
§6.8: "health checks") intentionally left out of this milestone -- the RI has
|
||||
no registry of backend health endpoints yet, and gating publication on one
|
||||
would just be a `TODO` masquerading as a check.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from emcp_bus.common.models import RiskTier
|
||||
from emcp_bus.registry.lifecycle import validate_transition
|
||||
from emcp_bus.registry.models import CapabilityManifest, LifecycleStatus
|
||||
from emcp_bus.registry.store import RegistryStore
|
||||
|
||||
_HIGH_IMPACT_TIERS = frozenset({RiskTier.R3, RiskTier.R4})
|
||||
|
||||
|
||||
class PublishingRejectedError(Exception):
|
||||
def __init__(self, reasons: list[str]) -> None:
|
||||
self.reasons = reasons
|
||||
super().__init__("; ".join(reasons))
|
||||
|
||||
|
||||
class PublishingPipeline:
|
||||
def __init__(self, store: RegistryStore) -> None:
|
||||
self._store = store
|
||||
|
||||
def submit(self, manifest: CapabilityManifest) -> CapabilityManifest:
|
||||
"""Runs `manifest` through the gates, then NOMINATE -> REVIEW -> REGISTER.
|
||||
|
||||
Stops short of `ACTIVE` -- a registered capability is not yet
|
||||
resolvable by the Fabric until a separate `publish()` call, mirroring
|
||||
README.md §23's Register and Publish being distinct stages.
|
||||
"""
|
||||
reasons = self._run_gates(manifest)
|
||||
if reasons:
|
||||
raise PublishingRejectedError(reasons)
|
||||
|
||||
nominated = manifest.model_copy(
|
||||
update={
|
||||
"lifecycle": manifest.lifecycle.model_copy(
|
||||
update={"status": LifecycleStatus.NOMINATE}
|
||||
)
|
||||
}
|
||||
)
|
||||
self._store.upsert(nominated)
|
||||
|
||||
validate_transition(LifecycleStatus.NOMINATE, LifecycleStatus.REVIEW)
|
||||
self._store.set_lifecycle_status(manifest.name, LifecycleStatus.REVIEW)
|
||||
|
||||
validate_transition(LifecycleStatus.REVIEW, LifecycleStatus.REGISTER)
|
||||
self._store.set_lifecycle_status(manifest.name, LifecycleStatus.REGISTER)
|
||||
|
||||
return self._store.get(manifest.name)
|
||||
|
||||
def publish(self, name: str) -> CapabilityManifest:
|
||||
"""REGISTER -> ACTIVE: the capability becomes resolvable (README.md §23 "Publish")."""
|
||||
manifest = self._store.get(name)
|
||||
validate_transition(manifest.lifecycle.status, LifecycleStatus.ACTIVE)
|
||||
self._store.set_lifecycle_status(name, LifecycleStatus.ACTIVE)
|
||||
return self._store.get(name)
|
||||
|
||||
def _run_gates(self, manifest: CapabilityManifest) -> list[str]:
|
||||
reasons: list[str] = []
|
||||
if not manifest.owner.strip():
|
||||
reasons.append("owner is required")
|
||||
if not manifest.backend.service.strip():
|
||||
reasons.append("backend.service is required")
|
||||
if manifest.risk_tier in _HIGH_IMPACT_TIERS and not manifest.approval.required:
|
||||
reasons.append(
|
||||
f"risk tier {manifest.risk_tier.value} requires approval.required=true "
|
||||
"(README.md §17)"
|
||||
)
|
||||
return reasons
|
||||
Reference in New Issue
Block a user