Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
576 lines
25 KiB
Python
576 lines
25 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from sqlalchemy import func
|
|
|
|
from govoplan_core.core.access import (
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
)
|
|
from govoplan_core.core.module_guards import (
|
|
drop_table_retirement_provider,
|
|
persistent_table_uninstall_guard,
|
|
)
|
|
from govoplan_core.core.modules import (
|
|
CapabilityDocumentation,
|
|
DocumentationCondition,
|
|
DocumentationLink,
|
|
DocumentationTopic,
|
|
FrontendModule,
|
|
FrontendRoute,
|
|
MigrationSpec,
|
|
ModuleContext,
|
|
ModuleInterfaceProvider,
|
|
ModuleManifest,
|
|
NavItem,
|
|
PermissionDefinition,
|
|
ProductAreaContribution,
|
|
RoleTemplate,
|
|
)
|
|
from govoplan_core.core.provider_governance import (
|
|
ModuleArchitectureDeclaration,
|
|
ModuleArchitectureDocumentation,
|
|
ModuleMaturityEvidence,
|
|
)
|
|
from govoplan_core.core.search import SearchSourceProviderRegistration
|
|
from govoplan_core.core.views import ViewSurface
|
|
from govoplan_core.db.base import Base
|
|
from govoplan_projects.backend.acl import ProjectScopeAclProvider
|
|
from govoplan_projects.backend.db import models as project_models
|
|
from govoplan_projects.backend.dsar_provider import (
|
|
PROJECTS_DSAR_CAPABILITY,
|
|
ProjectsDsarProvider,
|
|
)
|
|
from govoplan_projects.backend.search_source import create_projects_search_source
|
|
from govoplan_projects.backend.service import (
|
|
CAPABILITY_PROJECTS_REGISTRY,
|
|
SqlProjectRegistry,
|
|
)
|
|
|
|
|
|
MODULE_ID = "projects"
|
|
MODULE_NAME = "Projects"
|
|
MODULE_VERSION = "0.1.20"
|
|
READ_SCOPE = "projects:project:read"
|
|
WRITE_SCOPE = "projects:project:write"
|
|
ADMIN_SCOPE = "projects:project:admin"
|
|
OPTIONAL_DEPENDENCIES = (
|
|
"tasks",
|
|
"tickets",
|
|
"cases",
|
|
"wiki",
|
|
"calendar",
|
|
"files",
|
|
"workflow_engine",
|
|
"connectors",
|
|
"search",
|
|
"notifications",
|
|
"reporting",
|
|
"risk_compliance",
|
|
)
|
|
|
|
ARCHITECTURE = ModuleArchitectureDeclaration(
|
|
layer="domain_capability",
|
|
kind="domain",
|
|
maturity="vertical_slice",
|
|
evidence=(
|
|
ModuleMaturityEvidence(
|
|
kind="documentation",
|
|
reference="docs/PROJECTS_DOMAIN_BOUNDARY.md",
|
|
summary=(
|
|
"Defines portfolio, project, outcome, governance, and integration "
|
|
"ownership."
|
|
),
|
|
),
|
|
ModuleMaturityEvidence(
|
|
kind="test",
|
|
reference="tests/test_project_service.py",
|
|
summary=(
|
|
"Proves immutable revisions, OCC, idempotency, tenant isolation, "
|
|
"restricted memberships, state transitions, and search rechecks."
|
|
),
|
|
),
|
|
),
|
|
known_limits=(
|
|
"OpenProject transport and synchronization remain connector-owned provider work.",
|
|
"Task execution remains in Tasks or Tickets; Projects links work without duplicating its lifecycle.",
|
|
"The first WebUI edits core planning fields; advanced outcome, capacity, and benefit structures remain available through the governed API.",
|
|
),
|
|
supported_authority_modes=(
|
|
"native_authoritative",
|
|
"external_authoritative",
|
|
"external_mirror",
|
|
"governed_sync",
|
|
"governance_overlay",
|
|
"linked_reference",
|
|
),
|
|
owned_concepts=(
|
|
"portfolio and project identity",
|
|
"project goals and intended outcomes",
|
|
"project dependencies and capacity assumptions",
|
|
"project benefit and change-impact review",
|
|
),
|
|
non_owned_concepts=(
|
|
"actionable task and ticket lifecycle",
|
|
"risk and control lifecycle",
|
|
"report calculation and presentation",
|
|
"external project-system transport",
|
|
),
|
|
documentation=ModuleArchitectureDocumentation(
|
|
operations=("docs/PROJECTS_DOMAIN_BOUNDARY.md",),
|
|
),
|
|
)
|
|
|
|
|
|
def _router(context: ModuleContext):
|
|
del context
|
|
from govoplan_projects.backend.router import router
|
|
|
|
return router
|
|
|
|
|
|
def _registry(context: ModuleContext) -> SqlProjectRegistry:
|
|
del context
|
|
return SqlProjectRegistry()
|
|
|
|
|
|
def _dsar_provider(_context: ModuleContext) -> ProjectsDsarProvider:
|
|
return ProjectsDsarProvider()
|
|
|
|
|
|
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
|
|
counts = {
|
|
kind: count
|
|
for kind, count in (
|
|
session.query(
|
|
project_models.ProjectObjectRevision.object_kind,
|
|
func.count(),
|
|
)
|
|
.filter(
|
|
project_models.ProjectObjectRevision.tenant_id == tenant_id,
|
|
project_models.ProjectObjectRevision.superseded_at.is_(None),
|
|
)
|
|
.group_by(project_models.ProjectObjectRevision.object_kind)
|
|
.all()
|
|
)
|
|
}
|
|
return {
|
|
"portfolios": int(counts.get("portfolio", 0)),
|
|
"projects": int(counts.get("project", 0)),
|
|
"milestones": int(counts.get("milestone", 0)),
|
|
}
|
|
|
|
|
|
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=MODULE_NAME,
|
|
level="tenant",
|
|
module_id=module_id,
|
|
resource=resource,
|
|
action=action,
|
|
)
|
|
|
|
|
|
PERMISSIONS = (
|
|
_permission(
|
|
READ_SCOPE, "View projects", "Read accessible projects and portfolios."
|
|
),
|
|
_permission(
|
|
WRITE_SCOPE, "Manage projects", "Create and update projects and milestones."
|
|
),
|
|
_permission(
|
|
ADMIN_SCOPE, "Administer projects", "Configure project types and policies."
|
|
),
|
|
)
|
|
|
|
ROLE_TEMPLATES = (
|
|
RoleTemplate(
|
|
slug="projects_manager",
|
|
name="Projects manager",
|
|
description="Create and manage projects and milestones.",
|
|
permissions=(READ_SCOPE, WRITE_SCOPE),
|
|
),
|
|
RoleTemplate(
|
|
slug="projects_viewer",
|
|
name="Projects viewer",
|
|
description="Read accessible projects.",
|
|
permissions=(READ_SCOPE,),
|
|
),
|
|
)
|
|
|
|
DOCUMENTATION = (
|
|
DocumentationTopic(
|
|
id="projects.module-boundary",
|
|
title="Plan and coordinate projects",
|
|
summary=(
|
|
"Coordinate portfolios, projects, milestones, participants, dependencies, "
|
|
"capacity assumptions, outcomes, and benefit reviews without absorbing work-item ownership."
|
|
),
|
|
body=(
|
|
"Documentation books sit immediately beside the visible heading or contextual label for "
|
|
"Projects, not among operational action buttons. Field help remains beside its label. "
|
|
"Projects owns native portfolio and project context, immutable revisions of goals and intended outcomes, "
|
|
"milestones, participants, status, dependencies, capacity assumptions, benefit reviews, and governed resource references. "
|
|
"Create or revise the planning context, review its impact, and link provider-owned work and evidence without copying it. "
|
|
"Tasks and Tickets retain actionable work, Cases retains formal procedures, and Connectors retains OpenProject transport. "
|
|
"Reporting owns measured indicators and Risk Compliance owns risks and controls; Projects links those facts to planning, "
|
|
"change impact, and benefit review."
|
|
),
|
|
layer="available",
|
|
documentation_types=("admin", "user"),
|
|
audience=("user", "operator", "module_admin", "product_owner"),
|
|
conditions=(DocumentationCondition(required_scopes=(READ_SCOPE,)),),
|
|
related_modules=OPTIONAL_DEPENDENCIES,
|
|
links=(
|
|
DocumentationLink(
|
|
label="Repository domain boundary",
|
|
href="govoplan-projects/docs/PROJECTS_DOMAIN_BOUNDARY.md",
|
|
kind="repository",
|
|
),
|
|
DocumentationLink(
|
|
label="Projects interface pattern audit",
|
|
href="govoplan-projects/docs/INTERFACE_PATTERN_MIGRATION.md",
|
|
kind="repository",
|
|
),
|
|
),
|
|
metadata={
|
|
"kind": "workflow",
|
|
"help_contexts": [
|
|
"projects.workspace",
|
|
"projects.project",
|
|
"projects.milestone",
|
|
],
|
|
"purpose": (
|
|
"Maintain a revisioned planning context and connect provider-owned work, evidence, and measurements to it."
|
|
),
|
|
"prerequisites": [
|
|
"The actor can read the project; revisions require project write access.",
|
|
"Referenced work, cases, files, risks, reports, and external systems remain authorized by their owner providers.",
|
|
],
|
|
"steps": [
|
|
"Select a portfolio or create the project identity, status, participants, and planning horizon.",
|
|
"Record versioned goals, intended outcomes, milestones, dependencies, and capacity assumptions.",
|
|
"Link Tasks, Tickets, Cases, files, risks, controls, reports, or external project references without copying owner data.",
|
|
"Review status, change impact, milestone progress, and benefit evidence before appending a revision.",
|
|
"Use owner-module links to manage actionable work or formal procedures in their authoritative surface.",
|
|
],
|
|
"fields": {
|
|
"portfolio": "Groups projects for planning and oversight without changing project authority.",
|
|
"project": "The native revisioned context for outcome, status, participation, and resource links.",
|
|
"milestone": "A dated project checkpoint, not an actionable Task or formal Case state.",
|
|
"participant": "An explicit project access grant and role, independent of object ownership elsewhere.",
|
|
"external_reference": "A governed link to a provider-owned project or resource, including OpenProject mappings.",
|
|
},
|
|
"limitations": [
|
|
"Projects does not own Tasks, Tickets, Cases, source measurements, risks, controls, files, or external synchronization transport.",
|
|
"A linked object remains unavailable when its owner provider denies current access.",
|
|
],
|
|
"operational_consequences": {
|
|
"revise_plan": "Appends a new planning revision while preserving prior goals, status, and evidence links.",
|
|
"change_participation": "Changes project access only and does not grant access to linked provider-owned objects.",
|
|
"link_external_resource": "Stores a governed reference; synchronization remains a Connector responsibility.",
|
|
},
|
|
"verification": [
|
|
"The project revision names its portfolio, status, participants, milestones, and intended outcomes.",
|
|
"Every linked resource identifies its owner module or external provider and remains independently authorized.",
|
|
"Benefit and change-impact review preserves the revision and evidence references used for the decision.",
|
|
],
|
|
},
|
|
translations={
|
|
"de": {
|
|
"title": "Projekte planen und koordinieren",
|
|
"summary": (
|
|
"Portfolios, Projekte, Meilensteine, Beteiligte, Abhängigkeiten, Kapazitätsannahmen, "
|
|
"Ergebnisse und Nutzenprüfungen koordinieren, ohne die Eigentümerschaft an Arbeitselementen zu übernehmen."
|
|
),
|
|
"body": (
|
|
"Dokumentationsbücher stehen unmittelbar neben der sichtbaren Überschrift oder "
|
|
"Kontextbezeichnung für Projekte, nicht zwischen ausführbaren Aktionsschaltflächen. Feldhilfe "
|
|
"bleibt neben der Feldbezeichnung. "
|
|
"Projects führt den nativen Portfolio- und Projektkontext, unveränderliche Revisionen von Zielen und "
|
|
"beabsichtigten Ergebnissen, Meilensteine, Beteiligte, Status, Abhängigkeiten, Kapazitätsannahmen, "
|
|
"Nutzenprüfungen und gesteuerte Ressourcenreferenzen. Planungskontext wird angelegt oder revidiert, "
|
|
"seine Auswirkung geprüft und anbietergeführte Arbeit und Nachweise werden verknüpft, ohne sie zu kopieren. "
|
|
"Tasks und Tickets behalten ausführbare Arbeit, Cases formelle Verfahren und Connectors den OpenProject-Transport. "
|
|
"Reporting führt gemessene Kennzahlen und Risk Compliance Risiken und Kontrollen; Projects verknüpft diese Fakten "
|
|
"mit Planung, Änderungsfolgen und Nutzenprüfung."
|
|
),
|
|
}
|
|
},
|
|
structured_translation_version="1",
|
|
structured_translations={
|
|
"de": {
|
|
"purpose": (
|
|
"Einen revisionierten Planungskontext pflegen und anbietergeführte Arbeit, Nachweise und Messwerte damit verknüpfen."
|
|
),
|
|
"prerequisites": [
|
|
"Die handelnde Person darf das Projekt lesen; Revisionen erfordern Projektschreibberechtigung.",
|
|
"Verknüpfte Arbeit, Verfahren, Dateien, Risiken, Berichte und externe Systeme bleiben durch ihre Eigentümeranbieter autorisiert.",
|
|
],
|
|
"steps": [
|
|
"Ein Portfolio auswählen oder Projektidentität, Status, Beteiligte und Planungshorizont anlegen.",
|
|
"Revisionierte Ziele, beabsichtigte Ergebnisse, Meilensteine, Abhängigkeiten und Kapazitätsannahmen erfassen.",
|
|
"Tasks, Tickets, Cases, Dateien, Risiken, Kontrollen, Berichte oder externe Projektreferenzen verknüpfen, ohne Eigentümerdaten zu kopieren.",
|
|
"Status, Änderungsfolgen, Meilensteinfortschritt und Nutzennachweise prüfen, bevor eine Revision angefügt wird.",
|
|
"Verknüpfungen zu Eigentümermodulen verwenden, um ausführbare Arbeit oder formelle Verfahren in deren führender Oberfläche zu verwalten.",
|
|
],
|
|
"fields": {
|
|
"portfolio": "Gruppiert Projekte für Planung und Aufsicht, ohne die Projektzuständigkeit zu verändern.",
|
|
"project": "Der native revisionierte Kontext für Ergebnis, Status, Beteiligung und Ressourcenverknüpfungen.",
|
|
"milestone": "Ein datierter Projektprüfpunkt, kein ausführbarer Task und kein formeller Case-Status.",
|
|
"participant": "Eine ausdrückliche Projektzugriffsfreigabe und Rolle, unabhängig von Eigentümerschaft in anderen Modulen.",
|
|
"external_reference": "Eine gesteuerte Verknüpfung zu einem anbietergeführten Projekt oder einer Ressource, einschließlich OpenProject-Zuordnungen.",
|
|
},
|
|
"limitations": [
|
|
"Projects führt weder Tasks, Tickets, Cases, Quellmesswerte, Risiken, Kontrollen, Dateien noch externen Synchronisationstransport.",
|
|
"Ein verknüpftes Objekt bleibt unverfügbar, wenn sein Eigentümeranbieter den aktuellen Zugriff verweigert.",
|
|
],
|
|
"operational_consequences": {
|
|
"revise_plan": "Fügt eine neue Planungsrevision an und bewahrt frühere Ziele, Status und Nachweisverknüpfungen.",
|
|
"change_participation": "Ändert nur den Projektzugriff und gewährt keinen Zugriff auf verknüpfte anbietergeführte Objekte.",
|
|
"link_external_resource": "Speichert eine gesteuerte Referenz; Synchronisation bleibt Aufgabe eines Connectors.",
|
|
},
|
|
"verification": [
|
|
"Die Projektrevision nennt Portfolio, Status, Beteiligte, Meilensteine und beabsichtigte Ergebnisse.",
|
|
"Jede verknüpfte Ressource nennt Eigentümermodul oder externen Anbieter und bleibt unabhängig autorisiert.",
|
|
"Nutzen- und Änderungsfolgenprüfung bewahrt die für die Entscheidung verwendete Revision und Nachweisreferenzen.",
|
|
],
|
|
}
|
|
},
|
|
),
|
|
)
|
|
|
|
manifest = ModuleManifest(
|
|
id=MODULE_ID,
|
|
name=MODULE_NAME,
|
|
version=MODULE_VERSION,
|
|
dependencies=("access",),
|
|
optional_dependencies=OPTIONAL_DEPENDENCIES,
|
|
required_capabilities=(
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
),
|
|
permissions=PERMISSIONS,
|
|
role_templates=ROLE_TEMPLATES,
|
|
route_factory=_router,
|
|
nav_items=(
|
|
NavItem(
|
|
path="/projects",
|
|
label="Projects",
|
|
icon="folder-kanban",
|
|
required_any=(READ_SCOPE,),
|
|
order=36,
|
|
),
|
|
),
|
|
frontend=FrontendModule(
|
|
module_id=MODULE_ID,
|
|
package_name="@govoplan/projects-webui",
|
|
routes=(
|
|
FrontendRoute(
|
|
path="/projects",
|
|
component="ProjectsPage",
|
|
required_any=(READ_SCOPE,),
|
|
order=36,
|
|
),
|
|
),
|
|
nav_items=(
|
|
NavItem(
|
|
path="/projects",
|
|
label="Projects",
|
|
icon="folder-kanban",
|
|
required_any=(READ_SCOPE,),
|
|
order=36,
|
|
),
|
|
),
|
|
product_areas=(
|
|
ProductAreaContribution(
|
|
id="work",
|
|
module_id=MODULE_ID,
|
|
label="i18n:govoplan-core.product_area.work",
|
|
icon="list-checks",
|
|
description="i18n:govoplan-core.product_area.work_description",
|
|
surface_ids=("projects.nav.projects", "projects.route.projects"),
|
|
order=10,
|
|
),
|
|
),
|
|
view_surfaces=(
|
|
ViewSurface(
|
|
id="projects.navigation",
|
|
module_id=MODULE_ID,
|
|
kind="navigation",
|
|
label="Projects navigation",
|
|
order=10,
|
|
),
|
|
ViewSurface(
|
|
id="projects.workspace",
|
|
module_id=MODULE_ID,
|
|
kind="route",
|
|
label="Projects workspace",
|
|
order=20,
|
|
),
|
|
ViewSurface(
|
|
id="projects.portfolios",
|
|
module_id=MODULE_ID,
|
|
kind="section",
|
|
label="Portfolio planning",
|
|
parent_id="projects.workspace",
|
|
order=30,
|
|
),
|
|
ViewSurface(
|
|
id="projects.outcomes",
|
|
module_id=MODULE_ID,
|
|
kind="section",
|
|
label="Outcomes and benefits",
|
|
parent_id="projects.workspace",
|
|
order=40,
|
|
),
|
|
),
|
|
),
|
|
provides_interfaces=(
|
|
ModuleInterfaceProvider(name="projects.registry", version="0.1.0"),
|
|
ModuleInterfaceProvider(name=PROJECTS_DSAR_CAPABILITY, version="0.1.0"),
|
|
),
|
|
capability_factories={
|
|
CAPABILITY_PROJECTS_REGISTRY: _registry,
|
|
PROJECTS_DSAR_CAPABILITY: _dsar_provider,
|
|
},
|
|
capability_documentation={
|
|
CAPABILITY_PROJECTS_REGISTRY: CapabilityDocumentation(
|
|
label="Projects registry",
|
|
summary=(
|
|
"Persists portfolios, projects, milestones, planning intent, "
|
|
"membership grants, revisions, and lifecycle events."
|
|
),
|
|
contract_version="0.1.0",
|
|
),
|
|
PROJECTS_DSAR_CAPABILITY: CapabilityDocumentation(
|
|
label="Projects data-subject request provider",
|
|
summary=(
|
|
"Exports exact project memberships and minimized lifecycle attribution "
|
|
"without project payload content."
|
|
),
|
|
contract_version="0.1.0",
|
|
),
|
|
},
|
|
search_sources=(
|
|
SearchSourceProviderRegistration(
|
|
id="projects.objects",
|
|
factory=create_projects_search_source,
|
|
),
|
|
),
|
|
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(
|
|
project_models.ProjectObjectEvent,
|
|
project_models.ProjectMembershipGrant,
|
|
project_models.ProjectObjectRevision,
|
|
project_models.ProjectObjectIdentity,
|
|
label="Projects",
|
|
),
|
|
retirement_notes=(
|
|
"Destructive retirement requires a database snapshot and removes "
|
|
"Projects identities, immutable revisions, memberships, and events."
|
|
),
|
|
),
|
|
uninstall_guard_providers=(
|
|
persistent_table_uninstall_guard(
|
|
project_models.ProjectObjectIdentity,
|
|
project_models.ProjectObjectRevision,
|
|
project_models.ProjectMembershipGrant,
|
|
project_models.ProjectObjectEvent,
|
|
label="Projects",
|
|
),
|
|
),
|
|
resource_acl_providers=(
|
|
ProjectScopeAclProvider("portfolio"),
|
|
ProjectScopeAclProvider("project"),
|
|
ProjectScopeAclProvider("milestone"),
|
|
),
|
|
tenant_summary_providers=(_tenant_summary,),
|
|
documentation=(
|
|
DocumentationTopic(
|
|
id="projects.data-subject-requests",
|
|
title="Project data-subject requests",
|
|
summary=(
|
|
"Export project participation and accountable activity without project content."
|
|
),
|
|
body=(
|
|
"Projects correlates exact account and identity membership grants, plus "
|
|
"an explicitly supplied function-assignment reference. It separately "
|
|
"matches exact account, identity, and membership identifiers used for "
|
|
"creation and lifecycle attribution. Searches can narrow to one project, "
|
|
"portfolio, or milestone, but an object identifier alone never establishes "
|
|
"a subject. Membership exports include role, permissions, active state, "
|
|
"and source revision. Titles, search text, planning payloads, event payloads, "
|
|
"request hashes, idempotency keys, and unrelated members are excluded. "
|
|
"Membership removal requires manual access and ownership review; immutable "
|
|
"creation, revision, and event attribution remains retained governance evidence."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("user", "operator", "module_admin", "auditor"),
|
|
related_modules=("core", "access", "tasks", "audit"),
|
|
order=90,
|
|
metadata={
|
|
"kind": "reference",
|
|
"help_contexts": [
|
|
"projects.workspace",
|
|
"privacy.data-subject-requests",
|
|
],
|
|
"consequence_classes": {
|
|
"export_project_membership": "Returns exact subject grants without unrelated members.",
|
|
"review_membership_removal": "Requires ownership and access-continuity review.",
|
|
"retain_project_attribution": "Preserves immutable lifecycle accountability.",
|
|
},
|
|
},
|
|
translations={
|
|
"de": {
|
|
"title": "Datenschutzanfragen zu Projekten",
|
|
"summary": (
|
|
"Projektbeteiligung und verantwortbare Aktivitäten ohne Projektinhalte exportieren."
|
|
),
|
|
"body": (
|
|
"Projects gleicht exakte Konto- und Identitätsfreigaben sowie eine ausdrücklich angegebene "
|
|
"Funktionszuweisungsreferenz ab. Exakte Konto-, Identitäts- und Mitgliedschaftskennungen für "
|
|
"Anlage- und Lebenszykluszuschreibung werden getrennt ermittelt. Suchen können auf ein Projekt, "
|
|
"Portfolio oder einen Meilenstein eingegrenzt werden; eine Objektkennung allein begründet niemals "
|
|
"eine betroffene Person. Beteiligungsexporte enthalten Rolle, Berechtigungen, Aktivstatus und "
|
|
"Quellrevision. Titel, Suchtext, Planungs- und Ereignisinhalte, Anforderungsprüfsummen, "
|
|
"Idempotenzschlüssel und unbeteiligte Mitglieder bleiben ausgeschlossen. Die Entfernung einer "
|
|
"Beteiligung erfordert eine manuelle Prüfung von Zugriff und Eigentumsfortbestand; unveränderliche "
|
|
"Zuschreibungen zu Anlage, Revision und Ereignissen bleiben als Steuerungsnachweise erhalten."
|
|
),
|
|
}
|
|
},
|
|
structured_translation_version="1",
|
|
structured_translations={
|
|
"de": {
|
|
"consequence_classes": {
|
|
"export_project_membership": "Gibt exakte Betroffenenfreigaben ohne unbeteiligte Mitglieder zurück.",
|
|
"review_membership_removal": "Erfordert eine Prüfung von Eigentümerschaft und Zugriffskontinuität.",
|
|
"retain_project_attribution": "Bewahrt unveränderliche Lebenszyklusverantwortung auf.",
|
|
}
|
|
}
|
|
},
|
|
),
|
|
*DOCUMENTATION,
|
|
),
|
|
architecture=ARCHITECTURE,
|
|
)
|
|
|
|
|
|
def get_manifest() -> ModuleManifest:
|
|
return manifest
|