feat: add deployment zone overlays

This commit is contained in:
2026-05-24 15:55:05 +02:00
parent 62236f6453
commit ff1c4ce05b
28 changed files with 1282 additions and 26 deletions

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
import json
from typing import Any
from .deployment_overlay import normalize_deployment_overlay
FINANCIAL_API_VERSION = "railiance.fabric/v1alpha2"
FINANCIAL_SCHEMA_VERSION = "financial-fabric-v1"
@@ -19,7 +21,7 @@ RELATIONSHIP_CATEGORIES = {
"evidence",
}
FABRIC_KINDS = {"Fabric", "Subfabric"}
ENVIRONMENT_LABELS = {"local", "dev", "staging", "prod", "lab", "all"}
ENVIRONMENT_LABELS = {"local", "dev", "test", "staging", "prod", "lab", "all"}
def is_financial_graph_export(graph: dict[str, Any]) -> bool:
@@ -125,6 +127,7 @@ def financial_graph_errors(graph: dict[str, Any]) -> list[str]:
_validate_containment(errors, f"nodes[{index}]", node, netkingdom_id, fabric_kinds, accepted=review_state == "accepted")
_validate_ownership(errors, f"nodes[{index}]", node, actor_roles, accepted=review_state == "accepted")
_validate_optional_object(errors, f"nodes[{index}].accounting", node, "accounting")
_validate_overlay(errors, f"nodes[{index}].deployment_overlay", node)
edges = _indexed_items(errors, graph, "edges")
for index, edge in edges:
@@ -141,6 +144,7 @@ def financial_graph_errors(graph: dict[str, Any]) -> list[str]:
errors.append(f"{path}.relationship_category {category!r} is not valid")
_validate_evidence(errors, path, edge)
_validate_optional_object(errors, f"{path}.accounting", edge, "accounting")
_validate_overlay(errors, f"{path}.deployment_overlay", edge)
if edge_type == "provides_utility_to" and category != "utility":
errors.append(f"{path}.relationship_category must be 'utility' for provides_utility_to edges")
if category == "utility":
@@ -211,6 +215,13 @@ def _materialize_node(node: dict[str, Any]) -> None:
for key in ("containment", "ownership", "accounting"):
if key not in node and isinstance(attrs.get(key), dict):
node[key] = attrs[key]
overlay = normalize_deployment_overlay(
node.get("deployment_overlay") if isinstance(node.get("deployment_overlay"), dict) else {},
attrs,
node.get("containment") if isinstance(node.get("containment"), dict) else {},
)
if overlay:
node["deployment_overlay"] = overlay
node.setdefault("evidence", _legacy_evidence(node, attrs))
@@ -218,6 +229,12 @@ def _materialize_edge(edge: dict[str, Any]) -> None:
attrs = edge.get("attributes") if isinstance(edge.get("attributes"), dict) else {}
if "accounting" not in edge and isinstance(attrs.get("accounting"), dict):
edge["accounting"] = attrs["accounting"]
overlay = normalize_deployment_overlay(
edge.get("deployment_overlay") if isinstance(edge.get("deployment_overlay"), dict) else {},
attrs,
)
if overlay:
edge["deployment_overlay"] = overlay
edge.setdefault("relationship_category", _relationship_category(edge))
edge.setdefault("evidence", _legacy_evidence(edge, attrs))
if edge.get("relationship_category") == "utility":
@@ -362,6 +379,18 @@ def _validate_optional_object(errors: list[str], path: str, item: dict[str, Any]
errors.append(f"{path} must be an object")
def _validate_overlay(errors: list[str], path: str, item: dict[str, Any]) -> None:
overlay = item.get("deployment_overlay")
if overlay is None:
return
if not isinstance(overlay, dict):
errors.append(f"{path} must be an object")
return
route = overlay.get("route_evidence")
if route is not None and not isinstance(route, dict):
errors.append(f"{path}.route_evidence must be an object")
def _required_object(errors: list[str], path: str, item: dict[str, Any], key: str) -> dict[str, Any]:
value = item.get(key)
if not isinstance(value, dict):