feat(files): surface configured handbook tasks

This commit is contained in:
2026-07-21 18:54:26 +02:00
parent 1444ba80a0
commit 4722161592
6 changed files with 889 additions and 114 deletions
+152 -62
View File
@@ -25,6 +25,7 @@ from govoplan_core.core.modules import (
from govoplan_core.db.base import Base
from govoplan_files.backend.change_tracking import register_files_change_tracking
from govoplan_files.backend.db import models as file_models # noqa: F401 - populate Files ORM metadata
from govoplan_files.backend.documentation import documentation_topics
register_files_change_tracking()
@@ -195,114 +196,196 @@ manifest = ModuleManifest(
),
documentation=(
DocumentationTopic(
id="files.workflow.upload-organize-and-share",
title="Upload, organize, and share managed files",
summary="Put files in a personal or group space, organize them with explicit conflict handling, and grant or update access through a supported integration or API client.",
id="files.workflow.organize-managed-files",
title="Organize managed files and folders",
summary="Create folders and rename, move, or copy accessible managed content with explicit conflict handling.",
body=(
"Work in My files or an accessible group space. Uploads, folders, renames, moves, and copies stay inside governed managed storage and require an explicit choice when a target path already exists. "
"A caller with share permission can grant or update read, write, or manage access for a user, group, tenant, or campaign without changing ownership. The Files page does not yet provide a general share editor, and the API has no share-revocation route; sharing is currently performed by a supporting workflow or API client."
"Organization stays inside governed personal or group spaces. Moves preserve the asset identity, while copies create new assets and versions that reuse immutable blob bytes. "
"Every target conflict must be rejected, renamed, overwritten, or skipped explicitly."
),
layer="configured",
documentation_types=("user",),
audience=("file_user", "file_manager", "process_participant"),
order=39,
order=42,
conditions=(
DocumentationCondition(
required_modules=("files",),
required_scopes=(
"files:file:read",
"files:file:upload",
"files:file:organize",
"files:file:share",
),
required_scopes=("files:file:read", "files:file:organize"),
),
),
links=(
DocumentationLink(label="Files", href="/files", kind="runtime"),
DocumentationLink(label="Upload API", href="/api/v1/files/upload", kind="api"),
DocumentationLink(label="Move or copy API", href="/api/v1/files/transfer", kind="api"),
DocumentationLink(label="Grant or update a share", href="/api/v1/files/{file_id}/shares", kind="api"),
DocumentationLink(label="Files handbook", href="govoplan-files/docs/FILES_HANDBOOK.md", kind="repository"),
),
related_modules=("campaigns",),
unlocks=("Users and connected processes can exchange governed managed files without changing file ownership.",),
unlocks=("Managed content can be placed at stable logical paths without bypassing space access.",),
metadata={
"kind": "workflow",
"route": "/files",
"screen": "Files",
"help_contexts": ["files.list"],
"prerequisites": [
"Files is installed and you may read, upload, organize, and share managed files.",
"You can access the destination personal or group space.",
"A supporting workflow or API client is available when a user, group, tenant, or campaign share must be granted or updated.",
"You may view and organize managed files.",
"You can access every source and destination space used by the operation.",
],
"steps": [
"Open Files, choose My files or an accessible group space, and open the intended destination folder.",
"Create folders where needed, then upload or drag files into the destination.",
"Resolve every name conflict explicitly by rejecting, renaming, overwriting, or skipping the affected item.",
"Use rename, move, or copy to place the managed items at their intended logical paths.",
"Review the current version, checksum, owner, and path before granting access.",
"Through the supporting workflow or API, grant or update the required read, write, or manage share; do not promise revocation because no revoke route exists yet.",
"Have the intended recipient verify access, and verify that an unrelated account remains denied.",
"Open Files and select the personal or group space to organize.",
"Create the required destination folders or select the files and folders to rename, move, or copy.",
"Choose the destination and resolve each target conflict explicitly.",
"Apply the operation and reopen the destination folder.",
],
"outcome": "The content is stored as governed managed assets in the intended space and the requested access has been granted or updated without changing ownership.",
"verification": "Reopen the files to confirm owner, logical path, current version, and checksum, then test one intended and one denied access path. A share that must be removed requires an explicit future revocation capability.",
"outcome": "The selected content has the intended governed owner and logical path.",
"verification": "Confirm each resulting path and owner; for a move, also confirm the old path is gone, and for a copy, confirm the source remains.",
"related_topic_ids": [
"files.workflow.import-managed-snapshot",
"files.assurance.process-and-release-readiness",
"files.workflow.upload-managed-files",
"files.workflow.find-and-download-files",
"files.workflow.delete-managed-files",
],
},
),
DocumentationTopic(
id="files.workflow.find-and-download-files",
title="Find and download managed files",
summary="Search accessible managed content and download a current file version or a ZIP archive of a selection.",
body=(
"Files can be sorted and searched by logical path or name pattern. A download always uses the accessible current version; a multi-file selection can be generated as a temporary ZIP archive. "
"There is no dedicated content-preview service yet."
),
layer="configured",
documentation_types=("user",),
audience=("file_user", "file_manager", "process_participant"),
order=43,
conditions=(
DocumentationCondition(
required_modules=("files",),
required_scopes=("files:file:read", "files:file:download"),
),
),
links=(
DocumentationLink(label="Files", href="/files", kind="runtime"),
DocumentationLink(label="Files handbook", href="govoplan-files/docs/FILES_HANDBOOK.md", kind="repository"),
),
unlocks=("Authorized users can retrieve governed current versions without gaining organization or sharing authority.",),
metadata={
"kind": "workflow",
"route": "/files",
"screen": "Files",
"help_contexts": ["files.list"],
"prerequisites": ["You may view and download managed files in the relevant space."],
"steps": [
"Open Files and select the relevant personal or group space.",
"Navigate folders, sort the list, or use a path/name pattern to find the intended content.",
"Review the displayed owner, path, size, checksum, and version details.",
"Download one current file version, or select several files and choose Download ZIP.",
],
"outcome": "The authorized current file bytes or generated archive are downloaded to the local device.",
"verification": "Confirm the downloaded names and, where integrity matters, compare the file bytes with the displayed checksum.",
"related_topic_ids": [
"files.workflow.organize-managed-files",
"files.reference.snapshot-provenance-and-capabilities",
],
},
),
DocumentationTopic(
id="files.workflow.import-managed-snapshot",
title="Import an external file as a governed snapshot",
summary="Browse an authorized connection read-only, import one selected file into managed storage, and use the frozen version in another GovOPlaN task.",
id="files.workflow.share-managed-files",
title="Grant or update access to managed files",
summary="Grant or update read, write, or manage access through a supporting workflow or API client without changing ownership.",
body=(
"Choose a visible file connection in Files, browse only the permitted remote path, and import the selected content into your personal or group space. "
"The managed copy records source provenance and remains unchanged until an explicit manual sync. Browse, import, and sync never mutate the remote source."
"The Files service can grant or update a share for a user, group, tenant, or campaign. The Files page does not yet provide a general share editor, and the API has no share-revocation route. "
"Do not use this task when access must later be revoked until that explicit capability is implemented."
),
layer="configured",
layer="available",
documentation_types=("user",),
audience=("file_user", "campaign_manager", "report_author"),
order=40,
audience=("file_manager", "process_participant"),
order=44,
conditions=(
DocumentationCondition(
required_modules=("files",),
required_scopes=("files:file:read", "files:file:upload"),
required_scopes=("files:file:read", "files:file:share"),
),
),
links=(
DocumentationLink(label="Files", href="/files", kind="runtime"),
DocumentationLink(label="Visible connector profiles", href="/api/v1/files/connectors/profiles", kind="api"),
DocumentationLink(
label="Connector import API",
href="/api/v1/files/connectors/profiles/{profile_id}/import",
kind="api",
),
DocumentationLink(label="Grant or update a share", href="/api/v1/files/{file_id}/shares", kind="api"),
DocumentationLink(label="Files handbook", href="govoplan-files/docs/FILES_HANDBOOK.md", kind="repository"),
),
related_modules=("campaigns",),
unlocks=("Campaigns, reports, and workflows can consume a managed snapshot with stable source evidence.",),
unlocks=("A supporting process can grant governed file access without changing file ownership.",),
metadata={
"kind": "workflow",
"route": "/files",
"screen": "Files",
"help_contexts": ["files.list"],
"prerequisites": [
"Files is installed and you may read and upload files.",
"An administrator has configured a connector profile visible in your current user, group, tenant, or campaign scope.",
"You may view and share the managed file.",
"A supporting workflow or API client is available; the Files page has no general share editor yet.",
"The process does not require share revocation, because no revocation route exists yet.",
],
"steps": [
"Open Files and choose a managed destination space.",
"Choose Sync from connection, select a visible profile, and browse to the permitted remote file.",
"Import or sync the selected file and resolve any destination conflict explicitly.",
"Review the managed file's source and current-version details before using it in another task.",
"Open Files and verify the file owner, logical path, current version, and checksum.",
"Through the supporting workflow, identify the intended user, group, tenant, or campaign and choose read, write, or manage access.",
"Grant or update the share without changing file ownership.",
"Have the intended recipient verify access and an unrelated account verify denial.",
],
"outcome": "The external content is a tenant-managed snapshot with a checksum, exact version, and recorded source context.",
"verification": "Reopen the managed file and confirm its connector/provider, remote identity or path, source revision when available, checksum, and current version.",
"limitations": [
"The Files page has no general share editor.",
"Shares can be granted or updated, but not revoked through the current API.",
],
"outcome": "The requested access is granted or updated while the managed asset keeps its owner.",
"verification": "Test one intended and one denied access path. Do not claim that the share can later be revoked.",
"related_topic_ids": [
"files.governed-connectors-and-provenance",
"files.reference.integrity-recovery-and-fail-closed-transports",
"files.reference.snapshot-provenance-and-capabilities",
"files.workflow.find-and-download-files",
"files.assurance.process-and-release-readiness",
],
},
),
DocumentationTopic(
id="files.workflow.delete-managed-files",
title="Delete managed files and folders",
summary="Soft-delete accessible managed files or a folder tree where current policy allows it.",
body=(
"Deletion hides the selected managed assets rather than hard-purging their stored evidence. Folder deletion is recursive by default and includes child folders and files; a non-recursive request fails for a non-empty folder. "
"There is no self-service restore or hard-purge workflow today."
),
layer="configured",
documentation_types=("user",),
audience=("file_user", "file_manager", "process_participant"),
order=45,
conditions=(
DocumentationCondition(
required_modules=("files",),
required_scopes=("files:file:read", "files:file:delete"),
),
),
links=(
DocumentationLink(label="Files", href="/files", kind="runtime"),
DocumentationLink(label="Files handbook", href="govoplan-files/docs/FILES_HANDBOOK.md", kind="repository"),
),
unlocks=("Authorized users can remove obsolete content from active file views while preserving the current soft-delete boundary.",),
metadata={
"kind": "workflow",
"route": "/files",
"screen": "Files",
"help_contexts": ["files.list"],
"prerequisites": [
"You may view and delete the selected managed content.",
"You have reviewed the complete folder tree when deleting recursively.",
],
"steps": [
"Open Files and select the files or folder to delete.",
"Review the selection and, for a folder, all content below it.",
"Confirm the delete action.",
"Refresh or reopen the space and verify that the selected paths are no longer active.",
],
"limitations": [
"Deletion is soft deletion, not a hard purge.",
"There is no self-service restore or hard-purge workflow.",
],
"outcome": "The selected content is hidden from active Files views under the current soft-delete model.",
"verification": "Confirm the deleted paths no longer appear in the active space; do not treat the action as physical erasure.",
"related_topic_ids": [
"files.workflow.organize-managed-files",
"files.assurance.process-and-release-readiness",
],
},
),
@@ -317,7 +400,7 @@ manifest = ModuleManifest(
layer="configured",
documentation_types=("admin",),
audience=("file_admin", "tenant_admin", "system_admin", "security_auditor"),
order=41,
order=50,
conditions=(
DocumentationCondition(
required_modules=("files",),
@@ -374,7 +457,7 @@ manifest = ModuleManifest(
layer="configured",
documentation_types=("admin",),
audience=("operator", "system_admin", "security_auditor"),
order=42,
order=51,
conditions=(
DocumentationCondition(
required_modules=("files",),
@@ -428,7 +511,7 @@ manifest = ModuleManifest(
layer="available",
documentation_types=("admin", "user"),
audience=("module_integrator", "file_admin", "campaign_admin", "process_designer"),
order=43,
order=52,
conditions=(
DocumentationCondition(
required_modules=("files",),
@@ -467,7 +550,7 @@ manifest = ModuleManifest(
layer="configured",
documentation_types=("admin", "user"),
audience=("process_owner", "release_manager", "file_admin", "operator", "security_auditor"),
order=44,
order=53,
conditions=(
DocumentationCondition(
required_modules=("files",),
@@ -500,6 +583,7 @@ manifest = ModuleManifest(
"kind": "workflow",
"route": "/files",
"screen": "Files process and release assurance",
"help_contexts": ["files.list"],
"prerequisites": [
"A named process owner has defined the intended users, data classification, retention expectations, and integrations.",
"A candidate release is installed on a clean database and an upgrade copy with aligned Python, root package, WebUI package, and module-manifest versions.",
@@ -517,7 +601,12 @@ manifest = ModuleManifest(
"outcome": "The process or release has an explicit approval record tied to aligned versions, representative evidence, known limitations, and owned residual risks.",
"verification": "A reviewer can reproduce the recorded allow/deny, integrity, connector, and recovery checks and can trace each unmet requirement to a documented limitation or accepted compensating control.",
"related_topic_ids": [
"files.workflow.upload-organize-and-share",
"files.workflow.upload-managed-files",
"files.workflow.upload-and-unpack-zip",
"files.workflow.organize-managed-files",
"files.workflow.find-and-download-files",
"files.workflow.share-managed-files",
"files.workflow.delete-managed-files",
"files.workflow.import-managed-snapshot",
"files.governed-connectors-and-provenance",
"files.reference.integrity-recovery-and-fail-closed-transports",
@@ -526,6 +615,7 @@ manifest = ModuleManifest(
},
),
),
documentation_providers=(documentation_topics,),
migration_spec=MigrationSpec(
module_id="files",
metadata=Base.metadata,