feat: implement assurance graph and screening evidence

This commit is contained in:
2026-08-01 17:48:39 +02:00
parent bbf7288e14
commit a23b53dc9e
18 changed files with 4005 additions and 42 deletions
+136 -22
View File
@@ -26,9 +26,17 @@ from govoplan_core.core.modules import (
from govoplan_core.core.sanctions import (
CAPABILITY_RISK_COMPLIANCE_SANCTIONS_SCREENING,
)
from govoplan_core.core.search import SearchSourceProviderRegistration
from govoplan_core.core.provider_governance import (
ModuleArchitectureDeclaration,
ModuleArchitectureDocumentation,
ModuleMaturityEvidence,
)
from govoplan_core.core.views import ViewSurface
from govoplan_core.db.base import Base
from govoplan_risk_compliance.backend.db.models import (
RiskAssuranceEdge,
RiskAssuranceNode,
RiskSanctionsAddress,
RiskSanctionsAlias,
RiskSanctionsDate,
@@ -66,6 +74,8 @@ OPTIONAL_DEPENDENCIES = (
"connectors",
)
_PERSISTENT_MODELS = (
RiskAssuranceEdge,
RiskAssuranceNode,
RiskScreeningException,
RiskScreeningDisposition,
RiskScreeningCandidate,
@@ -79,6 +89,72 @@ _PERSISTENT_MODELS = (
RiskSanctionsListSnapshot,
)
ARCHITECTURE = ModuleArchitectureDeclaration(
layer="governance_accountability",
kind="governance",
maturity="vertical_slice",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_assurance_graph.py",
summary=(
"Exercises tenant-safe immutable graph revisions, bounded traversal, "
"sanctions projection, synthetic controls, ACL, and search."
),
),
ModuleMaturityEvidence(
kind="test",
reference="tests/test_sanctions_screening.py",
summary=(
"Exercises immutable sanctions evidence, deterministic matching, "
"review, exceptions, and freshness gates."
),
),
ModuleMaturityEvidence(
kind="migration",
reference="tests/test_migrations.py",
summary=(
"Exercises the persistent sanctions screening and assurance "
"graph schemas."
),
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md",
summary="Defines assurance ownership and integration boundaries.",
),
),
known_limits=(
"The assurance graph provides generic governance primitives; domain modules still own corrective execution.",
"Cross-tenant aggregate assurance is intentionally not exposed by the tenant API.",
),
supported_authority_modes=(
"native_authoritative",
"external_mirror",
"governance_overlay",
"linked_reference",
),
owned_concepts=(
"risk and control evaluation",
"sanctions screening runs",
"candidate review and dispositions",
"compliance findings and assurance review",
),
non_owned_concepts=(
"external source transport and credentials",
"immutable audit event storage",
"policy rule evaluation",
"governed domain objects and corrective execution",
),
documentation=ModuleArchitectureDocumentation(
migration=("tests/test_migrations.py",),
upgrade=("docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md",),
recovery=("docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md",),
security=("docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md",),
operations=("docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md",),
),
)
def _permission(
scope: str,
@@ -140,9 +216,7 @@ ROLE_TEMPLATES = (
RoleTemplate(
slug="risk_compliance_manager",
name="Risk Compliance manager",
description=(
"Manage compliance workflows and administer sanctions screening."
),
description=("Manage compliance workflows and administer sanctions screening."),
permissions=(
READ_SCOPE,
WRITE_SCOPE,
@@ -155,9 +229,7 @@ ROLE_TEMPLATES = (
RoleTemplate(
slug="risk_compliance_reviewer",
name="Risk Compliance reviewer",
description=(
"Run screenings and independently review potential matches."
),
description=("Run screenings and independently review potential matches."),
permissions=(
READ_SCOPE,
SANCTIONS_READ_SCOPE,
@@ -188,13 +260,19 @@ def _sanctions_screening_provider(_context):
return RiskComplianceSanctionsScreeningProvider()
def _assurance_search_source(context):
from govoplan_risk_compliance.backend.search_source import (
create_risk_assurance_search_source,
)
return create_risk_assurance_search_source(context)
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
return {
"risk_sanctions_list_snapshots": (
session.query(RiskSanctionsListSnapshot)
.filter(
RiskSanctionsListSnapshot.tenant_id == tenant_id
)
.filter(RiskSanctionsListSnapshot.tenant_id == tenant_id)
.count()
),
"risk_screening_runs": (
@@ -212,6 +290,14 @@ def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
)
.count()
),
"risk_assurance_nodes": (
session.query(RiskAssuranceNode)
.filter(
RiskAssuranceNode.tenant_id == tenant_id,
RiskAssuranceNode.superseded_at.is_(None),
)
.count()
),
}
@@ -227,7 +313,11 @@ DOCUMENTATION = (
"Connectors may acquire source evidence, but Risk Compliance "
"owns immutable normalized sanctions lists, version-pinned "
"screening, candidate review, and legal dispositions. Fuzzy "
"matching only creates candidates and never confirms a match."
"matching only creates candidates and never confirms a match. "
"The broader module direction links obligations, governed object "
"references, risks, controls, evidence, findings, corrective "
"measures, and effectiveness reviews without copying the governed "
"domain object or replacing Policy and Audit."
),
layer="available",
documentation_types=("admin", "user"),
@@ -242,8 +332,7 @@ DOCUMENTATION = (
DocumentationLink(
label="Repository domain boundary",
href=(
"govoplan-risk-compliance/"
"docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md"
"govoplan-risk-compliance/docs/RISK_COMPLIANCE_DOMAIN_BOUNDARY.md"
),
kind="repository",
),
@@ -260,6 +349,20 @@ DOCUMENTATION = (
"Queue and audit summaries contain stable references and "
"minimal subject data."
),
"assurance_domain_model": [
"obligation",
"governed object reference",
"risk",
"control",
"evidence",
"finding",
"corrective measure",
"effectiveness review",
],
"assurance_graph": (
"Every node and edge is effective-dated, revisioned, tenant-scoped, "
"and linked through opaque governed-object references."
),
},
),
)
@@ -292,10 +395,14 @@ manifest = ModuleManifest(
role_templates=ROLE_TEMPLATES,
route_factory=_route_factory,
capability_factories={
CAPABILITY_RISK_COMPLIANCE_SANCTIONS_SCREENING: (
_sanctions_screening_provider
),
CAPABILITY_RISK_COMPLIANCE_SANCTIONS_SCREENING: (_sanctions_screening_provider),
},
search_sources=(
SearchSourceProviderRegistration(
id="risk_compliance.assurance",
factory=_assurance_search_source,
),
),
frontend=FrontendModule(
module_id=MODULE_ID,
package_name="@govoplan/risk-compliance-webui",
@@ -303,7 +410,7 @@ manifest = ModuleManifest(
FrontendRoute(
path="/risk-compliance",
component="RiskCompliancePage",
required_any=(SANCTIONS_READ_SCOPE,),
required_any=(READ_SCOPE, SANCTIONS_READ_SCOPE),
order=115,
surface_id="risk_compliance.workspace",
),
@@ -313,7 +420,7 @@ manifest = ModuleManifest(
path="/risk-compliance",
label="Risk Compliance",
icon="shield-check",
required_any=(SANCTIONS_READ_SCOPE,),
required_any=(READ_SCOPE, SANCTIONS_READ_SCOPE),
order=115,
surface_id="risk_compliance.navigation",
),
@@ -340,15 +447,21 @@ manifest = ModuleManifest(
label="Sanctions review queue",
order=40,
),
ViewSurface(
id="risk_compliance.assurance.graph",
module_id=MODULE_ID,
kind="section",
label="Assurance graph",
order=50,
),
),
),
tenant_summary_providers=(_tenant_summary,),
architecture=ARCHITECTURE,
migration_spec=MigrationSpec(
module_id=MODULE_ID,
metadata=Base.metadata,
script_location=str(
Path(__file__).with_name("migrations") / "versions"
),
script_location=str(Path(__file__).with_name("migrations") / "versions"),
migration_after=("connectors",),
retirement_supported=True,
retirement_provider=drop_table_retirement_provider(
@@ -356,8 +469,9 @@ manifest = ModuleManifest(
label="Risk Compliance",
),
retirement_notes=(
"Destructive retirement removes immutable sanctions list, "
"screening, and review evidence after a database snapshot."
"Destructive retirement removes immutable assurance graph, "
"sanctions list, screening, and review evidence after a database "
"snapshot."
),
),
uninstall_guard_providers=(