feat: implement assurance graph and screening evidence
This commit is contained in:
@@ -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=(
|
||||
|
||||
Reference in New Issue
Block a user