330 lines
14 KiB
Python
330 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from govoplan_core.core.institutional import CAPABILITY_DECISION_REGISTRY
|
|
from govoplan_core.core.module_guards import (
|
|
drop_table_retirement_provider,
|
|
persistent_table_uninstall_guard,
|
|
)
|
|
from govoplan_core.core.modules import (
|
|
CapabilityDocumentation,
|
|
DocumentationLink,
|
|
DocumentationTopic,
|
|
MigrationSpec,
|
|
ModuleContext,
|
|
ModuleInterfaceProvider,
|
|
ModuleManifest,
|
|
PermissionDefinition,
|
|
RoleTemplate,
|
|
)
|
|
from govoplan_core.core.provider_governance import declared_module_architecture
|
|
from govoplan_core.core.search import SearchSourceProviderRegistration
|
|
from govoplan_core.db.base import Base
|
|
from govoplan_decisions.backend.db import models as decision_models
|
|
from govoplan_decisions.backend.dsar_provider import (
|
|
DECISIONS_DSAR_CAPABILITY,
|
|
DecisionsDsarProvider,
|
|
)
|
|
from govoplan_decisions.backend.record_source import (
|
|
CAPABILITY_RECORD_SOURCE_DECISIONS,
|
|
create_decisions_record_source,
|
|
)
|
|
from govoplan_decisions.backend.service import SqlDecisionRegistry
|
|
from govoplan_decisions.backend.search_source import create_decisions_search_source
|
|
|
|
|
|
MODULE_ID = "decisions"
|
|
MODULE_NAME = "Decisions"
|
|
MODULE_VERSION = "0.1.19"
|
|
READ_SCOPE = "decisions:decision:read"
|
|
SENSITIVE_READ_SCOPE = "decisions:decision:read_sensitive"
|
|
WRITE_SCOPE = "decisions:decision:write"
|
|
ADMIN_SCOPE = "decisions:decision:admin"
|
|
|
|
|
|
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
|
|
module_id, resource, action = scope.split(":", 2)
|
|
return PermissionDefinition(
|
|
scope=scope,
|
|
label=label,
|
|
description=description,
|
|
category="Decisions",
|
|
level="tenant",
|
|
module_id=module_id,
|
|
resource=resource,
|
|
action=action,
|
|
)
|
|
|
|
|
|
def _router(_context: ModuleContext):
|
|
from govoplan_decisions.backend.router import router
|
|
|
|
return router
|
|
|
|
|
|
def _registry(_context: ModuleContext) -> SqlDecisionRegistry:
|
|
return SqlDecisionRegistry()
|
|
|
|
|
|
def _dsar_provider(_context: ModuleContext) -> DecisionsDsarProvider:
|
|
return DecisionsDsarProvider()
|
|
|
|
|
|
manifest = ModuleManifest(
|
|
id=MODULE_ID,
|
|
name=MODULE_NAME,
|
|
version=MODULE_VERSION,
|
|
optional_dependencies=(
|
|
"mandates",
|
|
"approvals",
|
|
"committee",
|
|
"cases",
|
|
"audit",
|
|
"policy",
|
|
"records",
|
|
"files",
|
|
"postbox",
|
|
"mail",
|
|
),
|
|
provides_interfaces=(
|
|
ModuleInterfaceProvider(name="decisions.formal_outcome", version="0.1.0"),
|
|
ModuleInterfaceProvider(name="decisions.reconstruction", version="0.1.0"),
|
|
ModuleInterfaceProvider(
|
|
name=CAPABILITY_RECORD_SOURCE_DECISIONS, version="1.0.0"
|
|
),
|
|
ModuleInterfaceProvider(name=DECISIONS_DSAR_CAPABILITY, version="0.1.0"),
|
|
),
|
|
permissions=(
|
|
_permission(
|
|
READ_SCOPE,
|
|
"View Decision metadata",
|
|
"View formal Decision metadata and evidence references.",
|
|
),
|
|
_permission(
|
|
SENSITIVE_READ_SCOPE,
|
|
"View protected Decisions",
|
|
"View protected reasoning, operative results, and conditions.",
|
|
),
|
|
_permission(
|
|
WRITE_SCOPE,
|
|
"Record Decisions",
|
|
"Record governed formal outcomes and lifecycle revisions.",
|
|
),
|
|
_permission(
|
|
ADMIN_SCOPE,
|
|
"Administer Decisions",
|
|
"Administer Decision access, lifecycle, and recovery.",
|
|
),
|
|
),
|
|
role_templates=(
|
|
RoleTemplate(
|
|
slug="decision_officer",
|
|
name="Decision officer",
|
|
description="Record and inspect formal Decisions.",
|
|
permissions=(READ_SCOPE, SENSITIVE_READ_SCOPE, WRITE_SCOPE),
|
|
),
|
|
RoleTemplate(
|
|
slug="decision_auditor",
|
|
name="Decision auditor",
|
|
description="Reconstruct protected formal outcomes.",
|
|
permissions=(READ_SCOPE, SENSITIVE_READ_SCOPE),
|
|
),
|
|
),
|
|
route_factory=_router,
|
|
capability_factories={
|
|
CAPABILITY_DECISION_REGISTRY: _registry,
|
|
CAPABILITY_RECORD_SOURCE_DECISIONS: create_decisions_record_source,
|
|
DECISIONS_DSAR_CAPABILITY: _dsar_provider,
|
|
},
|
|
capability_documentation={
|
|
CAPABILITY_DECISION_REGISTRY: CapabilityDocumentation(
|
|
label="Formal Decision registry",
|
|
summary="Records and resolves immutable, reconstructable formal outcomes.",
|
|
contract_version="0.1.0",
|
|
),
|
|
CAPABILITY_RECORD_SOURCE_DECISIONS: CapabilityDocumentation(
|
|
label="Formal Decision record source",
|
|
summary="Resolves complete, currently authorized immutable Decision revisions for Records filing.",
|
|
contract_version="1.0.0",
|
|
),
|
|
DECISIONS_DSAR_CAPABILITY: CapabilityDocumentation(
|
|
label="Decisions data-subject request provider",
|
|
summary=(
|
|
"Exports minimized formal-Decision creator attribution without "
|
|
"protected reasoning, outcomes, conditions, or payloads."
|
|
),
|
|
contract_version="0.1.0",
|
|
),
|
|
},
|
|
migration_spec=MigrationSpec(
|
|
module_id=MODULE_ID,
|
|
metadata=Base.metadata,
|
|
script_location=str(Path(__file__).with_name("migrations") / "versions"),
|
|
retirement_supported=True,
|
|
retirement_provider=drop_table_retirement_provider(
|
|
decision_models.FormalDecisionRevision, label="Decisions"
|
|
),
|
|
retirement_notes="Destructive retirement requires a database snapshot and removes formal Decision history.",
|
|
),
|
|
uninstall_guard_providers=(
|
|
persistent_table_uninstall_guard(
|
|
decision_models.FormalDecisionRevision, label="Decisions"
|
|
),
|
|
),
|
|
search_sources=(
|
|
SearchSourceProviderRegistration(
|
|
id="decisions.decisions",
|
|
factory=create_decisions_search_source,
|
|
),
|
|
),
|
|
documentation=(
|
|
DocumentationTopic(
|
|
id="decisions.data-subject-requests",
|
|
title="Formal Decision data-subject requests",
|
|
summary=(
|
|
"Export minimized creator attribution from immutable formal Decision "
|
|
"revisions without exposing protected Decision content."
|
|
),
|
|
body=(
|
|
"Decisions searches exact account, membership, identity, or explicit "
|
|
"actor identifiers inside the active tenant. An optional Decision "
|
|
"identifier only narrows that verified actor search and cannot disclose "
|
|
"a Decision by itself. The access record contains revision, type, state, "
|
|
"valid-time, recorded-time, and creator activity only. Protected "
|
|
"reasoning, operative results, conditions, evidence payloads, and "
|
|
"digests remain excluded. Formal Decision history is immutable legal "
|
|
"and institutional evidence, so the provider publishes retain-only "
|
|
"erasure outcomes and never rewrites a revision."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("user", "operator", "module_admin", "auditor"),
|
|
related_modules=("core", "approvals", "committee", "records"),
|
|
metadata={
|
|
"help_contexts": ["privacy.data-subject-requests"],
|
|
"consequence_classes": {
|
|
"export_creator_attribution": (
|
|
"Returns lifecycle and creator context without protected payloads."
|
|
),
|
|
"retain_formal_history": (
|
|
"Preserves immutable institutional and legal evidence."
|
|
),
|
|
},
|
|
},
|
|
translations={
|
|
"de": {
|
|
"title": "Datenschutzanfragen zu formellen Entscheidungen",
|
|
"summary": (
|
|
"Minimierte Angaben zur erstellenden Person aus unveränderlichen "
|
|
"Entscheidungsrevisionen exportieren, ohne geschützte Inhalte offenzulegen."
|
|
),
|
|
"body": (
|
|
"Decisions sucht innerhalb des aktiven Mandanten nach exakten Konto-, "
|
|
"Mitgliedschafts-, Identitäts- oder ausdrücklich angegebenen Akteurskennungen. "
|
|
"Eine optionale Entscheidungskennung schränkt nur diese bereits verifizierte "
|
|
"Akteurssuche ein und kann für sich allein keine Entscheidung offenlegen. Der "
|
|
"Ausgabedatensatz enthält ausschließlich Revision, Typ, Status, Gültigkeits- und "
|
|
"Aufzeichnungszeit sowie die Aktivität der erstellenden Person. Geschützte "
|
|
"Begründungen, verfügende Ergebnisse, Bedingungen, Nachweisinhalte und Prüfsummen "
|
|
"bleiben ausgeschlossen. Die Historie formeller Entscheidungen ist unveränderlicher "
|
|
"rechtlicher und institutioneller Nachweis; deshalb veröffentlicht der Anbieter "
|
|
"ausschließlich Aufbewahrungsergebnisse und schreibt keine Revision um."
|
|
),
|
|
}
|
|
},
|
|
structured_translation_version="1",
|
|
structured_translations={
|
|
"de": {
|
|
"consequence_classes": {
|
|
"export_creator_attribution": (
|
|
"Gibt Lebenszyklus- und Erstellerkontext ohne geschützte Inhalte zurück."
|
|
),
|
|
"retain_formal_history": (
|
|
"Bewahrt unveränderliche institutionelle und rechtliche Nachweise auf."
|
|
),
|
|
}
|
|
}
|
|
},
|
|
),
|
|
DocumentationTopic(
|
|
id="decisions.formal-outcome",
|
|
title="Formal institutional Decisions",
|
|
summary="Reconstruct authority, evidence, reasoning, outcome, effects, and review history.",
|
|
body="Decisions preserves immutable formal outcomes. Corrections and revocations create linked revisions; requested and observed effects remain distinct for reconciliation. List and unversioned detail reads follow the titlebar valid-time and recorded-time selection; exact revision references remain exact and current authorization is unchanged. When Records is enabled, filing resolves one exact complete Decision revision only after current metadata and protected-read permissions are rechecked; Records retains the digest-bound source reference while Decisions remains authoritative. When Search is enabled, Decisions contributes only rebuildable lifecycle, subject, legal-basis, effect, and linked-Case metadata. Operative results, reasoning, and conditions are excluded, and every result receives a current permission check before disclosure.",
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("user", "operator", "module_admin", "auditor"),
|
|
links=(
|
|
DocumentationLink(
|
|
label="Decisions domain and recovery",
|
|
href="govoplan-decisions/docs/DECISIONS_DOMAIN.md",
|
|
kind="repository",
|
|
),
|
|
),
|
|
metadata={
|
|
"kind": "reference",
|
|
"help_contexts": [
|
|
"decisions.search.result",
|
|
"decisions.record.filing",
|
|
],
|
|
},
|
|
translations={
|
|
"de": {
|
|
"title": "Formelle institutionelle Entscheidungen",
|
|
"summary": (
|
|
"Zuständigkeit, Nachweise, Begründung, Ergebnis, Wirkungen und "
|
|
"Überprüfungshistorie nachvollziehbar rekonstruieren."
|
|
),
|
|
"body": (
|
|
"Decisions bewahrt formelle Ergebnisse als unveränderliche Revisionen auf. "
|
|
"Berichtigungen und Widerrufe erzeugen verknüpfte Revisionen; beabsichtigte und "
|
|
"beobachtete Wirkungen bleiben für den Abgleich getrennt. Listen und nicht "
|
|
"versionierte Detailansichten folgen der in der Titelleiste gewählten Gültigkeits- "
|
|
"und Aufzeichnungszeit; exakte Revisionsverweise bleiben exakt und die aktuelle "
|
|
"Berechtigungsprüfung gilt unverändert. Ist Records aktiviert, wird nur eine exakte, "
|
|
"vollständige Entscheidungsrevision abgelegt, nachdem aktuelle Metadaten- und "
|
|
"Leseberechtigungen erneut geprüft wurden. Records bewahrt den prüfsummengebundenen "
|
|
"Quellverweis, während Decisions maßgeblich bleibt. Ist Search aktiviert, trägt "
|
|
"Decisions ausschließlich wiederaufbaubare Lebenszyklus-, Betreff-, Rechtsgrundlagen-, "
|
|
"Wirkungs- und Fallverknüpfungsmetadaten bei. Verfügende Ergebnisse, Begründungen und "
|
|
"Bedingungen bleiben ausgeschlossen; vor jeder Offenlegung wird die aktuelle "
|
|
"Berechtigung erneut geprüft."
|
|
),
|
|
}
|
|
},
|
|
),
|
|
),
|
|
architecture=declared_module_architecture(
|
|
layer="governance_accountability",
|
|
kind="domain",
|
|
maturity="vertical_slice",
|
|
documentation_ref="docs/DECISIONS_DOMAIN.md",
|
|
test_ref="tests/test_decisions.py",
|
|
known_limits=(
|
|
"No dedicated Decision WebUI is included; consuming procedure modules present outcomes in context.",
|
|
),
|
|
supported_authority_modes=("native_authoritative",),
|
|
owned_concepts=(
|
|
"formal decision",
|
|
"decision correction",
|
|
"decision effect observation",
|
|
),
|
|
non_owned_concepts=(
|
|
"approval gate",
|
|
"committee deliberation",
|
|
"effect execution",
|
|
"record binary",
|
|
),
|
|
reference_packages=("product.service-to-decision",),
|
|
migration_docs=("docs/DECISIONS_DOMAIN.md",),
|
|
recovery_docs=("docs/DECISIONS_DOMAIN.md",),
|
|
security_docs=("docs/DECISIONS_DOMAIN.md",),
|
|
operations_docs=("docs/DECISIONS_DOMAIN.md",),
|
|
),
|
|
)
|
|
|
|
|
|
def get_manifest() -> ModuleManifest:
|
|
return manifest
|