feat(tasks): add governed DSAR coverage

This commit is contained in:
2026-08-21 04:21:01 +02:00
parent 39bb6c0d18
commit 88bd0e6aae
3 changed files with 856 additions and 2 deletions
+70 -2
View File
@@ -34,6 +34,10 @@ from govoplan_core.core.tasks import (
from govoplan_core.core.views import ViewSurface
from govoplan_core.db.base import Base
from govoplan_tasks.backend.db import models as task_models
from govoplan_tasks.backend.dsar_provider import (
TASKS_DSAR_CAPABILITY,
TasksDsarProvider,
)
from govoplan_tasks.backend.service import SqlTaskService
@@ -69,6 +73,10 @@ def _service(context: ModuleContext) -> SqlTaskService:
return SqlTaskService(context.registry)
def _dsar_provider(_context: ModuleContext) -> TasksDsarProvider:
return TasksDsarProvider()
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
total = (
session.query(task_models.TaskItem)
@@ -122,6 +130,54 @@ ROLE_TEMPLATES = (
)
DOCUMENTATION = (
DocumentationTopic(
id="tasks.data-subject-requests",
title="Task data-subject requests",
summary=(
"Export account-assigned task data and lifecycle attribution while "
"keeping shared institutional work under owner review."
),
body=(
"Tasks correlates exact account and actor identifiers only inside the "
"active tenant. Account-assigned explicit Tasks contribute bounded "
"title, summary, required action, lifecycle state, due dates, the exact "
"matching account assignment, source references, and any create, update, "
"or completion activities performed by the subject. Group, role, function, "
"and anyone visibility is not inferred from external directories and other "
"assignment targets are excluded. When the subject acted on a Task without "
"being its direct account assignee, only minimized lifecycle attribution "
"and source identity are exported. Provenance, arbitrary metadata, request "
"hashes, idempotency keys, and source-module payloads are excluded; source "
"references are never traversed. Task attribution is retained as immutable "
"accountability evidence. Assignment or content erasure requires manual "
"source-owner and retention review because a Task can be shared institutional "
"work; the provider performs no automatic mutation."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "tenant_admin", "operator", "auditor"),
related_modules=("core", "workflow_engine", "approvals", "notifications"),
metadata={
"help_contexts": [
"tasks.page.inbox",
"tasks.page.detail",
"tasks.field.assignment",
"privacy.data-subject-requests",
],
"consequence_classes": {
"export_assigned_task": (
"Returns bounded Task-owned data and exact account assignment only."
),
"review_assignment_erasure": (
"Requires the Task source owner and retention authority before "
"changing shared work."
),
"retain_actor_attribution": (
"Preserves minimized Task lifecycle accountability evidence."
),
},
},
),
DocumentationTopic(
id="tasks.quick-access-and-product-area",
title="Work in product navigation and Quick Access",
@@ -232,6 +288,7 @@ manifest = ModuleManifest(
provides_interfaces=(
ModuleInterfaceProvider(name=CAPABILITY_TASK_COMMANDS, version="1.0.0"),
ModuleInterfaceProvider(name="tasks.work_items", version="1.0.0"),
ModuleInterfaceProvider(name=TASKS_DSAR_CAPABILITY, version="0.1.0"),
),
permissions=PERMISSIONS,
role_templates=ROLE_TEMPLATES,
@@ -336,13 +393,24 @@ manifest = ModuleManifest(
),
),
tenant_summary_providers=(_tenant_summary,),
capability_factories={CAPABILITY_TASK_COMMANDS: _service},
capability_factories={
CAPABILITY_TASK_COMMANDS: _service,
TASKS_DSAR_CAPABILITY: _dsar_provider,
},
capability_documentation={
CAPABILITY_TASK_COMMANDS: CapabilityDocumentation(
label="Task commands",
summary="Creates replay-safe explicit tasks without importing the Tasks implementation.",
contract_version="1.0.0",
)
),
TASKS_DSAR_CAPABILITY: CapabilityDocumentation(
label="Tasks data-subject request provider",
summary=(
"Exports account-assigned Tasks and minimized actor attribution "
"with governed non-executable erasure outcomes."
),
contract_version="0.1.0",
),
},
work_item_providers=(
WorkItemProviderRegistration(id="tasks.explicit", factory=_service, order=10),