feat: add institutional governance and recovery contracts

This commit is contained in:
2026-08-01 17:46:54 +02:00
parent b65b48832b
commit 7192d32e65
61 changed files with 12539 additions and 168 deletions
+26 -1
View File
@@ -18,6 +18,7 @@ from govoplan_core.core.events import (
normalize_trace_id,
emit_platform_event,
)
from govoplan_core.core.institutional import GovernedContextEnvelope
from govoplan_core.core.runtime import get_registry
from govoplan_core.privacy.retention import sanitize_audit_details_for_policy
from govoplan_core.security.redaction import redact_secret_values
@@ -176,6 +177,16 @@ def _publish_audit_platform_event(
item: AuditRecordRef,
) -> None:
trace = _compact_trace(item.details.get("_trace") if isinstance(item.details, Mapping) else None)
raw_context = (
item.details.get("_institutional_context")
if isinstance(item.details, Mapping)
else None
)
institutional_context = (
GovernedContextEnvelope.from_mapping(raw_context)
if isinstance(raw_context, Mapping)
else None
)
emit_platform_event(
session,
PlatformEvent(
@@ -194,6 +205,7 @@ def _publish_audit_platform_event(
tenant=EventTenantRef(id=item.tenant_id) if item.tenant_id else None,
resource=EventObjectRef(type=item.object_type, id=item.object_id) if item.object_type else None,
classification="internal",
institutional_context=institutional_context,
)
)
@@ -211,6 +223,7 @@ def audit_event(
details: dict[str, Any] | None = None,
correlation_id: str | None = None,
causation_id: str | None = None,
institutional_context: GovernedContextEnvelope | None = None,
commit: bool = False,
) -> AuditRecordRef:
"""Persist one audit event.
@@ -223,8 +236,17 @@ def audit_event(
if scope not in {"tenant", "system"}:
raise ValueError(f"Unsupported audit scope: {scope}")
if (
institutional_context is not None
and tenant_id is not None
and institutional_context.tenant_id != tenant_id
):
raise ValueError("Audit institutional context belongs to another tenant")
raw_details = dict(details or {})
if institutional_context is not None:
raw_details["_institutional_context"] = institutional_context.to_dict()
traced_details, trace = _trace_details(
_sanitize_details(details or {}),
_sanitize_details(raw_details),
correlation_id=correlation_id,
causation_id=causation_id,
)
@@ -242,6 +264,7 @@ def audit_event(
api_key_id=api_key_id,
resource_type=object_type,
resource_id=object_id,
institutional_context=institutional_context,
details=stored_details,
))
record_change(
@@ -273,6 +296,7 @@ def audit_from_principal(
details: dict[str, Any] | None = None,
correlation_id: str | None = None,
causation_id: str | None = None,
institutional_context: GovernedContextEnvelope | None = None,
commit: bool = False,
) -> AuditRecordRef:
return audit_event(
@@ -287,5 +311,6 @@ def audit_from_principal(
details=details,
correlation_id=correlation_id,
causation_id=causation_id,
institutional_context=institutional_context,
commit=commit,
)