from __future__ import annotations from pathlib import Path from govoplan_core.core.access import ( CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER, ) from govoplan_core.core.institutional import CAPABILITY_FORM_DEFINITIONS from govoplan_core.core.module_guards import ( drop_table_retirement_provider, persistent_table_uninstall_guard, ) from govoplan_core.core.modules import ( CapabilityDocumentation, DocumentationLink, DocumentationTopic, FrontendModule, FrontendRoute, MigrationSpec, ModuleContext, ModuleInterfaceProvider, ModuleManifest, NavItem, PermissionDefinition, RoleTemplate, ) from govoplan_core.core.provider_governance import declared_module_architecture from govoplan_core.core.views import ViewSurface from govoplan_core.db.base import Base from govoplan_forms.backend.db import models as form_models from govoplan_forms.backend.service import SqlFormDefinitionProvider MODULE_ID = "forms" MODULE_NAME = "Forms" MODULE_VERSION = "0.1.17" READ_SCOPE = "forms:definition:read" WRITE_SCOPE = "forms:definition:write" ADMIN_SCOPE = "forms:definition:admin" OPTIONAL_DEPENDENCIES = ( "forms_runtime", "portal", "workflow_engine", "cases", "policy", ) 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, ) def _router(_context: ModuleContext): from govoplan_forms.backend.router import router return router def _definitions(_context: ModuleContext) -> SqlFormDefinitionProvider: return SqlFormDefinitionProvider() 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, ), provides_interfaces=( ModuleInterfaceProvider(name="forms.definitions", version="0.1.0"), ), permissions=( _permission( READ_SCOPE, "View form definitions", "Read reusable form definitions and exact revisions.", ), _permission( WRITE_SCOPE, "Manage form definitions", "Create and revise reusable form definitions.", ), _permission( ADMIN_SCOPE, "Publish form definitions", "Publish and retire form-definition revisions.", ), ), role_templates=( RoleTemplate( slug="forms_designer", name="Forms designer", description="Design and publish reusable form definitions.", permissions=(READ_SCOPE, WRITE_SCOPE, ADMIN_SCOPE), ), RoleTemplate( slug="forms_reader", name="Forms reader", description="Inspect reusable form definitions.", permissions=(READ_SCOPE,), ), ), route_factory=_router, nav_items=( NavItem( path="/forms", label="Form definitions", icon="list-tree", required_any=(READ_SCOPE,), order=36, ), ), frontend=FrontendModule( module_id=MODULE_ID, package_name="@govoplan/forms-webui", routes=( FrontendRoute( path="/forms", component="FormsPage", required_any=(READ_SCOPE,), order=36, ), ), nav_items=( NavItem( path="/forms", label="Form definitions", icon="list-tree", required_any=(READ_SCOPE,), order=36, ), ), view_surfaces=( ViewSurface( id="forms.navigation", module_id=MODULE_ID, kind="navigation", label="Form definitions navigation", order=10, ), ViewSurface( id="forms.catalogue", module_id=MODULE_ID, kind="route", label="Form definition catalogue", order=20, ), ), ), capability_factories={CAPABILITY_FORM_DEFINITIONS: _definitions}, capability_documentation={ CAPABILITY_FORM_DEFINITIONS: CapabilityDocumentation( label="Immutable form definitions", summary="Resolves exact tenant-bound form schemas without exposing Forms tables.", 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( form_models.FormDefinitionRevision, label=MODULE_NAME, ), retirement_notes="Destructive retirement removes immutable form-definition history and requires a database snapshot.", ), uninstall_guard_providers=( persistent_table_uninstall_guard( form_models.FormDefinitionRevision, label=MODULE_NAME, ), ), documentation=( DocumentationTopic( id="forms.definitions", title="Reusable form definitions", summary="Create immutable, versioned schemas consumed by Forms Runtime and institutional services.", body=( "Each revision fixes field types, options, constraints, draft, attachment, signature, policy, and handoff requirements. " "Publishing is explicit; existing submissions continue to retain their exact revision." ), layer="configured", documentation_types=("admin", "user"), audience=("user", "operator", "module_admin", "product_owner"), links=( DocumentationLink( label="Forms boundary and recovery", href="govoplan-forms/docs/FORMS_BOUNDARY.md", kind="repository", ), ), metadata={ "seed": True, "help_contexts": [ "forms.navigation", "forms.catalogue", "forms.state.permission-blocked", "forms.state.empty", ], "privacy_notes": [ "Definition catalogues contain schemas and policy references, not submitted Form values.", "Package assessment does not grant access to referenced runtime submissions or external providers.", "Published accessibility and localization content is visible wherever the exact definition is authorized.", ], }, ), DocumentationTopic( id="forms.reference.fields-and-consequences", title="Form definition fields and lifecycle consequences", summary="Schema, publication, localization, policy, evidence, package, and handoff semantics for immutable Form revisions.", body=( "The stable key identifies the definition while each save creates a new immutable revision. Field keys, types, " "constraints, visibility conditions, pages, sections, options, help, localization, and accessibility instructions " "become the exact runtime schema. Attachment, signature, policy, draft, and permitted-handoff settings are enforced " "by Forms Runtime when that module is present. Publishing makes a revision available for new instances; existing " "instances retain their prior exact revision. Retirement prevents future use without deleting definitions or submissions. " "Package import always creates a local draft and retains source provenance; it never silently publishes an imported revision." ), layer="configured", documentation_types=("admin", "user"), audience=("user", "operator", "module_admin", "auditor"), related_modules=OPTIONAL_DEPENDENCIES, links=( DocumentationLink( label="Forms boundary and recovery", href="govoplan-forms/docs/FORMS_BOUNDARY.md", kind="repository", ), ), metadata={ "seed": True, "help_contexts": [ "forms.field.publication-state", "forms.field.signature-requirement", "forms.field.policy-references", "forms.field.handoff-kinds", "forms.field.accessibility", "forms.field.change-reason", "forms.action.save-revision", "forms.action.publish", "forms.action.retire", "forms.action.import-package", ], "consequence_classes": { "save_revision": "Creates an immutable definition revision with a change reason.", "publish": "Makes the exact revision available for future authorized instances.", "retire": "Stops future use while retaining definitions and exact runtime references.", "import_package": "Creates a local draft and retains package provenance without automatic publication.", }, }, ), ), architecture=declared_module_architecture( layer="human_work_procedure", kind="domain", maturity="vertical_slice", documentation_ref="docs/FORMS_BOUNDARY.md", test_ref="tests/test_forms.py", known_limits=( "Concrete attachment/signature providers, anonymous identity profiles, richer authoring ergonomics, and target-produced accessibility evidence remain product depth.", ), supported_authority_modes=("native_authoritative",), owned_concepts=("form definition", "form schema", "form definition revision"), non_owned_concepts=( "form submission", "file content", "case", "workflow instance", ), reference_packages=("product.service-to-decision",), migration_docs=("docs/FORMS_BOUNDARY.md",), recovery_docs=("docs/FORMS_BOUNDARY.md",), security_docs=("docs/FORMS_BOUNDARY.md",), operations_docs=("docs/FORMS_BOUNDARY.md",), ), ) def get_manifest() -> ModuleManifest: return manifest