feat: explain configured institutional architecture

This commit is contained in:
2026-08-01 17:48:31 +02:00
parent 0d8a49c8af
commit 6607a3eeae
10 changed files with 421 additions and 15 deletions
+2
View File
@@ -27,5 +27,7 @@ tools/checks/check-focused.sh
- Keep documentation-layer behavior in this module, not core. - Keep documentation-layer behavior in this module, not core.
- Read configuration, module manifests, routes, permissions, and capability metadata through core contracts. - Read configuration, module manifests, routes, permissions, and capability metadata through core contracts.
- Do not import feature-module internals. Modules should contribute documentation metadata through manifests, capabilities, generated docs, or typed DTOs. - Do not import feature-module internals. Modules should contribute documentation metadata through manifests, capabilities, generated docs, or typed DTOs.
- Treat documentation as part of every behavior change. Update the owning module's manifest-driven `DocumentationTopic` contributions for each affected user and administrator workflow, setting, permission, limitation, and operational consequence.
- Require every module manifest to provide a static user and administrator documentation baseline, even when configured-state details come from `documentation_providers`. Validate the complete workspace with `/mnt/DATA/git/govoplan/tools/checks/check-manifest-shapes.py`.
- Prefer role-aware and configuration-aware documentation over global manuals. - Prefer role-aware and configuration-aware documentation over global manuals.
- Keep active backlog state in Gitea issues; keep durable context in repository docs and synced wiki pages. - Keep active backlog state in Gitea issues; keep durable context in repository docs and synced wiki pages.
+22
View File
@@ -55,6 +55,21 @@ Frontend package:
Platform module manifests, configuration packages, release catalogs, and governance rules are documented in `govoplan-core/docs/`. Platform module manifests, configuration packages, release catalogs, and governance rules are documented in `govoplan-core/docs/`.
Every module manifest must contribute a static documentation baseline for both
the `user` and `admin` projections through `ModuleManifest.documentation`.
Runtime providers may add actor- and configuration-specific detail, but they do
not replace that baseline. A behavior change is complete only when the owning
module updates the affected workflows, settings, permissions, limitations, and
operational consequences. Validate workspace coverage with:
```sh
cd /mnt/DATA/git/govoplan
./tools/checks/check-manifest-shapes.py
```
Feature content remains in the owning module. Docs indexes and renders the
contributions without importing feature implementations.
Capabilities can provide generic documentation without exposing their runtime Capabilities can provide generic documentation without exposing their runtime
provider implementation: provider implementation:
@@ -79,6 +94,13 @@ freshness, provenance, descriptions, and package requirements remain visible
as typed evidence; provider objects, credentials, and secret configuration are as typed evidence; provider objects, credentials, and secret configuration are
never imported into the Docs WebUI. never imported into the Docs WebUI.
Module-owned external-provider runtime state is also projected through the
Core contract. Administrative documentation may show sanitized binding-level
state; ordinary-user documentation receives only aggregate configured, active,
authority, health, freshness, conflict, recovery, and observation fields.
URLs, credential references, provider error text, and binding identifiers are
excluded from the user projection.
## Concept documents ## Concept documents
- `docs/DOCUMENTATION_LAYER_CONCEPT.md` defines the configured, available, and evidence documentation model. - `docs/DOCUMENTATION_LAYER_CONCEPT.md` defines the configured, available, and evidence documentation model.
+19
View File
@@ -93,6 +93,25 @@ register durable topics directly in its `ModuleManifest.documentation` tuple.
Use this for stable explanations such as the module purpose, common workflows, Use this for stable explanations such as the module purpose, common workflows,
policy hierarchy, route meaning, and links to public docs or repository docs. policy hierarchy, route meaning, and links to public docs or repository docs.
Every manifest must retain at least one static topic for each of the `user` and
`admin` projections. A shared topic may serve both only when its language and
disclosure level are appropriate for both audiences. A module that is still a
seed should state that limitation plainly rather than documenting an unfinished
screen as available. `documentation_providers` enrich this baseline; they do
not replace it because a provider may be unavailable before configuration or
database access succeeds.
Documentation is part of a behavior change's completion criteria. The owning
module updates affected workflows, fields and settings, permissions, optional
integration behavior, failure or limitation explanations, and operator
consequences in the same change. The workspace manifest-shape check enforces
the static audience baseline:
```sh
cd /mnt/DATA/git/govoplan
./tools/checks/check-manifest-shapes.py
```
When the text depends on active configuration, a module registers a provider in When the text depends on active configuration, a module registers a provider in
`ModuleManifest.documentation_providers`. The provider receives a `ModuleManifest.documentation_providers`. The provider receives a
`DocumentationContext` with the active registry, principal, settings, and a `DocumentationContext` with the active registry, principal, settings, and a
+130 -3
View File
@@ -20,6 +20,10 @@ from govoplan_core.core.modules import (
user_workflow_scope_condition_issues, user_workflow_scope_condition_issues,
) )
from govoplan_core.core.registry import PlatformRegistry from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.core.provider_governance import (
ExternalProviderStateContext,
collect_external_provider_states,
)
from govoplan_core.core.versioning import ( from govoplan_core.core.versioning import (
format_version_range, format_version_range,
version_satisfies_range, version_satisfies_range,
@@ -57,6 +61,7 @@ def docs_context(
detail="Administrative documentation requires documentation-administrator authority", detail="Administrative documentation requires documentation-administrator authority",
) )
registry = _registry(request) registry = _registry(request)
external_provider_states = _external_provider_states(registry, principal)
target_version = version if isinstance(version, str) else None target_version = version if isinstance(version, str) else None
resolved_locale = _preferred_locale(request, locale) resolved_locale = _preferred_locale(request, locale)
route_items = _route_items(registry.manifests(), principal) route_items = _route_items(registry.manifests(), principal)
@@ -84,12 +89,14 @@ def docs_context(
catalog = _admin_documentation_catalog( catalog = _admin_documentation_catalog(
registry, registry,
principal, principal,
external_provider_states=external_provider_states,
route_items=route_items, route_items=route_items,
visible_route_items=visible_route_items, visible_route_items=visible_route_items,
) )
else: else:
catalog = _user_documentation_catalog( catalog = _user_documentation_catalog(
registry, registry,
external_provider_states=external_provider_states,
visible_route_items=visible_route_items, visible_route_items=visible_route_items,
documentation_layers=documentation_layers, documentation_layers=documentation_layers,
) )
@@ -193,12 +200,17 @@ def _admin_documentation_catalog(
registry: PlatformRegistry, registry: PlatformRegistry,
principal: ApiPrincipal, principal: ApiPrincipal,
*, *,
external_provider_states: Mapping[str, Mapping[str, object]],
route_items: list[dict[str, Any]], route_items: list[dict[str, Any]],
visible_route_items: list[dict[str, Any]], visible_route_items: list[dict[str, Any]],
) -> dict[str, list[dict[str, Any]]]: ) -> dict[str, list[dict[str, Any]]]:
return { return {
"modules": [ "modules": [
_module_payload(manifest, technical=True) _module_payload(
manifest,
technical=True,
external_provider_states=external_provider_states,
)
for manifest in registry.manifests() for manifest in registry.manifests()
], ],
"permissions": [ "permissions": [
@@ -214,6 +226,7 @@ def _admin_documentation_catalog(
def _user_documentation_catalog( def _user_documentation_catalog(
registry: PlatformRegistry, registry: PlatformRegistry,
*, *,
external_provider_states: Mapping[str, Mapping[str, object]],
visible_route_items: list[dict[str, Any]], visible_route_items: list[dict[str, Any]],
documentation_layers: Mapping[str, list[dict[str, Any]]], documentation_layers: Mapping[str, list[dict[str, Any]]],
) -> dict[str, list[dict[str, Any]]]: ) -> dict[str, list[dict[str, Any]]]:
@@ -226,7 +239,11 @@ def _user_documentation_catalog(
} }
return { return {
"modules": [ "modules": [
_module_payload(manifest, technical=False) _module_payload(
manifest,
technical=False,
external_provider_states=external_provider_states,
)
for manifest in registry.manifests() for manifest in registry.manifests()
if manifest.id in visible_module_ids if manifest.id in visible_module_ids
], ],
@@ -314,6 +331,13 @@ def _documentation_summary(
permissions = catalog["permissions"] permissions = catalog["permissions"]
return { return {
"module_count": len(catalog["modules"]), "module_count": len(catalog["modules"]),
"architecture_declared_module_count": sum(
1 for item in catalog["modules"] if item.get("architecture")
),
"external_provider_count": sum(
int(item.get("external_provider_count") or 0)
for item in catalog["modules"]
),
"visible_route_count": len(catalog["visible_routes"]), "visible_route_count": len(catalog["visible_routes"]),
"available_route_count": len(catalog["available_routes"]), "available_route_count": len(catalog["available_routes"]),
"permission_count": len(permissions), "permission_count": len(permissions),
@@ -371,9 +395,34 @@ def _registry(request: Request) -> PlatformRegistry:
return registry return registry
def _module_payload(manifest: ModuleManifest, *, technical: bool) -> dict[str, Any]: def _module_payload(
manifest: ModuleManifest,
*,
technical: bool,
external_provider_states: Mapping[str, Mapping[str, object]] | None = None,
) -> dict[str, Any]:
frontend = manifest.frontend frontend = manifest.frontend
migration = manifest.migration_spec migration = manifest.migration_spec
architecture = manifest.architecture.to_dict() if manifest.architecture else None
if architecture is not None and not technical:
architecture = {
"contract_version": architecture["contract_version"],
"layer": architecture["layer"],
"kind": architecture["kind"],
"maturity": architecture["maturity"],
"known_limits": architecture["known_limits"],
"supported_authority_modes": architecture[
"supported_authority_modes"
],
"owned_concepts": architecture["owned_concepts"],
"non_owned_concepts": architecture["non_owned_concepts"],
"reference_packages": architecture["reference_packages"],
"target_tested_providers": architecture[
"target_tested_providers"
],
"evidence": [],
"documentation": {},
}
return { return {
"id": manifest.id, "id": manifest.id,
"name": manifest.name, "name": manifest.name,
@@ -390,9 +439,87 @@ def _module_payload(manifest: ModuleManifest, *, technical: bool) -> dict[str, A
"capabilities": sorted(manifest.capability_factories) if technical else [], "capabilities": sorted(manifest.capability_factories) if technical else [],
"documentation_count": len(manifest.documentation) if technical else 0, "documentation_count": len(manifest.documentation) if technical else 0,
"documentation_provider_count": len(manifest.documentation_providers) if technical else 0, "documentation_provider_count": len(manifest.documentation_providers) if technical else 0,
"architecture": architecture,
"external_provider_count": len(manifest.external_providers),
"external_providers": [
{
**(
declaration.to_dict()
if technical
else {
"id": declaration.id,
"module_id": declaration.module_id,
"label": declaration.label,
"maturity": declaration.maturity,
"operations": list(declaration.operations),
"authority_modes": list(declaration.authority_modes),
"known_outage_behavior": declaration.behavior.outage,
}
),
"runtime_state": _documentation_provider_state(
(external_provider_states or {}).get(declaration.id),
technical=technical,
),
}
for declaration in manifest.external_providers
],
} }
def _documentation_provider_state(
state: Mapping[str, object] | None,
*,
technical: bool,
) -> dict[str, object] | None:
if state is None:
return None
if technical:
return dict(state)
return {
key: state.get(key)
for key in (
"configured",
"active",
"authority_mode",
"authority_modes",
"health",
"freshness",
"conflict",
"recovery",
"observed_at",
)
}
def _external_provider_states(
registry: PlatformRegistry,
principal: ApiPrincipal,
) -> dict[str, dict[str, object]]:
registrations = registry.external_provider_state_providers()
if not registrations:
return {}
tenant_id = str(principal.tenant_id or "").strip() or None
try:
with get_database().session() as session:
return collect_external_provider_states(
registrations,
ExternalProviderStateContext(
session=session,
tenant_id=tenant_id,
principal=principal,
),
)
except Exception: # noqa: BLE001 - return sanitized per-provider diagnostics.
return collect_external_provider_states(
registrations,
ExternalProviderStateContext(
session=None,
tenant_id=tenant_id,
principal=principal,
),
)
def _user_route_payload(item: Mapping[str, Any]) -> dict[str, Any]: def _user_route_payload(item: Mapping[str, Any]) -> dict[str, Any]:
return { return {
"module_id": item["module_id"], "module_id": item["module_id"],
+133 -11
View File
@@ -1,6 +1,9 @@
from __future__ import annotations from __future__ import annotations
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER from govoplan_core.core.access import (
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
)
from govoplan_core.core.modules import ( from govoplan_core.core.modules import (
DocumentationCondition, DocumentationCondition,
DocumentationLink, DocumentationLink,
@@ -14,12 +17,48 @@ from govoplan_core.core.modules import (
PermissionDefinition, PermissionDefinition,
RoleTemplate, RoleTemplate,
) )
from govoplan_core.core.provider_governance import (
ModuleArchitectureDeclaration,
ModuleArchitectureDocumentation,
ModuleMaturityEvidence,
)
DOCS_READ_SCOPE = "docs:documentation:read" DOCS_READ_SCOPE = "docs:documentation:read"
DOCS_ADMIN_READ_SCOPE = "docs:documentation:admin" DOCS_ADMIN_READ_SCOPE = "docs:documentation:admin"
DOCS_ADMIN_READ_SCOPES = (DOCS_ADMIN_READ_SCOPE, "system:settings:read", "admin:settings:read") DOCS_ADMIN_READ_SCOPES = (
DOCS_ADMIN_READ_SCOPE,
"system:settings:read",
"admin:settings:read",
)
DOCS_READ_SCOPES = (DOCS_READ_SCOPE, *DOCS_ADMIN_READ_SCOPES) DOCS_READ_SCOPES = (DOCS_READ_SCOPE, *DOCS_ADMIN_READ_SCOPES)
ARCHITECTURE = ModuleArchitectureDeclaration(
layer="governance_accountability",
kind="presentation",
maturity="vertical_slice",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_docs_context.py",
summary="Tests audience-safe configured documentation and architecture projections.",
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/DOCUMENTATION_LAYER_CONCEPT.md",
summary="Defines the manifest-driven documentation boundary.",
),
),
known_limits=(
"Architecture declarations are in staged adoption, so undeclared modules remain visible as pending.",
),
owned_concepts=("configured documentation projection", "documentation audience filtering"),
non_owned_concepts=("module feature behavior", "module evidence generation"),
documentation=ModuleArchitectureDocumentation(
security=("docs/DOCUMENTATION_LAYER_CONCEPT.md",),
operations=("docs/DOCUMENTATION_LAYER_CONCEPT.md",),
),
)
def _permission(scope: str, label: str, description: str) -> PermissionDefinition: def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
module_id, resource, action = scope.split(":", 2) module_id, resource, action = scope.split(":", 2)
@@ -46,7 +85,10 @@ manifest = ModuleManifest(
id="docs", id="docs",
name="Docs", name="Docs",
version="0.1.10", version="0.1.10",
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), required_capabilities=(
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
),
optional_dependencies=("policy", "audit", "ops", "workflow_engine", "search"), optional_dependencies=("policy", "audit", "ops", "workflow_engine", "search"),
permissions=( permissions=(
_permission( _permission(
@@ -76,12 +118,35 @@ manifest = ModuleManifest(
), ),
), ),
route_factory=_route_factory, route_factory=_route_factory,
nav_items=(NavItem(path="/docs", label="Docs", icon="reports", required_any=DOCS_READ_SCOPES, order=880),), nav_items=(
NavItem(
path="/docs",
label="Docs",
icon="reports",
required_any=DOCS_READ_SCOPES,
order=880,
),
),
frontend=FrontendModule( frontend=FrontendModule(
module_id="docs", module_id="docs",
package_name="@govoplan/docs-webui", package_name="@govoplan/docs-webui",
routes=(FrontendRoute(path="/docs", component="DocsPage", required_any=DOCS_READ_SCOPES, order=880),), routes=(
nav_items=(NavItem(path="/docs", label="Docs", icon="reports", required_any=DOCS_READ_SCOPES, order=880),), FrontendRoute(
path="/docs",
component="DocsPage",
required_any=DOCS_READ_SCOPES,
order=880,
),
),
nav_items=(
NavItem(
path="/docs",
label="Docs",
icon="reports",
required_any=DOCS_READ_SCOPES,
order=880,
),
),
), ),
documentation=( documentation=(
DocumentationTopic( DocumentationTopic(
@@ -120,6 +185,49 @@ manifest = ModuleManifest(
), ),
metadata={"kind": "system"}, metadata={"kind": "system"},
), ),
DocumentationTopic(
id="docs.reference.institutional-governance-architecture",
title="Institutional governance architecture",
summary="GovOPlaN models institutional responsibility, governed work, formal outcomes, evidence, and external-system authority without turning every concept into Core or one monolithic application.",
body=(
"Organizations, Identity, IDM, Access, and Policy answer different parts of who may act. "
"Mandate, service, procedure-party, and formal-decision semantics are being introduced as shared contracts and become modules only after independent lifecycle and reuse are proven. "
"External integrations separately declare technical maturity and whether GovOPlaN is authoritative, mirrors an external source, synchronizes under governance, adds an overlay, or retains only a link."
),
layer="always",
documentation_types=("admin",),
audience=("tenant_admin", "operator", "module_admin", "product_owner"),
order=15,
translations={
"de": {
"title": "Architektur der institutionellen Steuerung",
"summary": "GovOPlaN modelliert institutionelle Verantwortung, gesteuerte Arbeit, formale Ergebnisse, Nachweise und die Datenhoheit externer Systeme, ohne alle Begriffe in den Kern oder eine monolithische Anwendung zu ziehen.",
"body": "Organisationen, Identitaeten, IDM, Zugriff und Richtlinien beantworten unterschiedliche Teile der Frage, wer handeln darf. Mandate, Leistungen, Verfahrensbeteiligte und formale Entscheidungen beginnen als gemeinsame Vertraege und werden erst bei nachgewiesenem eigenstaendigem Lebenszyklus zu Modulen. Integrationen erklaeren technische Reife und Datenhoheit getrennt.",
},
},
links=(
DocumentationLink(
label="Institutional governance target architecture",
href="govoplan/docs/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md",
kind="repository",
),
DocumentationLink(
label="Core module architecture",
href="govoplan-core/docs/MODULE_ARCHITECTURE.md",
kind="repository",
),
),
metadata={
"kind": "reference",
"architecture_topics": [
"institutional context",
"module ownership",
"source authority",
"integration maturity",
"product packages",
],
},
),
DocumentationTopic( DocumentationTopic(
id="docs.pattern.field-help", id="docs.pattern.field-help",
title="Field help marker", title="Field help marker",
@@ -177,19 +285,32 @@ manifest = ModuleManifest(
), ),
), ),
links=( links=(
DocumentationLink(label="Organizations", href="/organizations", kind="runtime"), DocumentationLink(
label="Organizations", href="/organizations", kind="runtime"
),
DocumentationLink(label="IDM assignments", href="/idm", kind="runtime"), DocumentationLink(label="IDM assignments", href="/idm", kind="runtime"),
DocumentationLink(label="Access administration", href="/admin", kind="runtime"), DocumentationLink(
label="Access administration", href="/admin", kind="runtime"
),
), ),
metadata={ metadata={
"kind": "reference", "kind": "reference",
"admin_explanation": "Function-to-role effects are owned by Access. IDM assignment changes can be governed independently from organization model changes.", "admin_explanation": "Function-to-role effects are owned by Access. IDM assignment changes can be governed independently from organization model changes.",
"user_explanation": "A person can hold a function because IDM links their identity to the organization function. Access decides which application permissions that function gives.", "user_explanation": "A person can hold a function because IDM links their identity to the organization function. Access decides which application permissions that function gives.",
"module_boundaries": [ "module_boundaries": [
{"module": "organizations", "owns": "unit types, structures, relations, units, and function definitions"}, {
"module": "organizations",
"owns": "unit types, structures, relations, units, and function definitions",
},
{"module": "identity", "owns": "identities and account links"}, {"module": "identity", "owns": "identities and account links"},
{"module": "idm", "owns": "identity-to-function assignments, delegation, acting-for links, and synchronization mapping"}, {
{"module": "access", "owns": "roles, permissions, and accepted function-to-role mappings"}, "module": "idm",
"owns": "identity-to-function assignments, delegation, acting-for links, and synchronization mapping",
},
{
"module": "access",
"owns": "roles, permissions, and accepted function-to-role mappings",
},
], ],
}, },
), ),
@@ -210,6 +331,7 @@ manifest = ModuleManifest(
}, },
), ),
), ),
architecture=ARCHITECTURE,
) )
-1
View File
@@ -8,7 +8,6 @@ from pydantic import BaseModel, ConfigDict, Field
from govoplan_core.core.modules import ( from govoplan_core.core.modules import (
DocumentationCondition, DocumentationCondition,
DocumentationLink,
DocumentationSourceDefinition, DocumentationSourceDefinition,
DocumentationType, DocumentationType,
ModuleManifest, ModuleManifest,
+61
View File
@@ -18,12 +18,18 @@ from govoplan_core.core.modules import (
FrontendRoute, FrontendRoute,
) )
from govoplan_core.core.registry import PlatformRegistry from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.core.provider_governance import (
ModuleArchitectureDeclaration,
ModuleMaturityEvidence,
)
from govoplan_tenancy.backend.manifest import get_manifest as get_tenancy_manifest from govoplan_tenancy.backend.manifest import get_manifest as get_tenancy_manifest
from govoplan_docs.backend.api.v1.routes import ( from govoplan_docs.backend.api.v1.routes import (
_classify_documentation, _classify_documentation,
_condition_visibility, _condition_visibility,
_documentation_topic_anchor, _documentation_topic_anchor,
_documentation_topic_groups, _documentation_topic_groups,
_documentation_provider_state,
_module_payload,
_visible_documentation_sources, _visible_documentation_sources,
docs_context, docs_context,
) )
@@ -46,6 +52,61 @@ class FakePrincipal:
class DocsContextTests(unittest.TestCase): class DocsContextTests(unittest.TestCase):
def test_user_provider_state_omits_binding_details(self) -> None:
state = {
"configured": True,
"active": True,
"health": "healthy",
"freshness": "current",
"conflict": "clear",
"recovery": "ready",
"binding_ref": "calendar:sync-source:secret-context",
"bindings": [{"binding_ref": "calendar:sync-source:secret-context"}],
}
user = _documentation_provider_state(state, technical=False)
technical = _documentation_provider_state(state, technical=True)
self.assertNotIn("binding_ref", user)
self.assertNotIn("bindings", user)
self.assertEqual("healthy", user["health"])
self.assertIn("bindings", technical)
def test_module_architecture_projection_hides_evidence_from_user_docs(self) -> None:
manifest = ModuleManifest(
id="example",
name="Example",
version="1.0.0",
architecture=ModuleArchitectureDeclaration(
layer="governance_accountability",
kind="governance",
maturity="vertical_slice",
evidence=(
ModuleMaturityEvidence(
kind="test",
reference="tests/test_example.py",
summary="Private implementation evidence.",
),
ModuleMaturityEvidence(
kind="documentation",
reference="docs/EXAMPLE.md",
summary="Architecture explanation.",
),
),
known_limits=("Example limit.",),
supported_authority_modes=("linked_reference",),
owned_concepts=("examples",),
),
)
technical = _module_payload(manifest, technical=True)
user = _module_payload(manifest, technical=False)
self.assertEqual("vertical_slice", technical["architecture"]["maturity"])
self.assertEqual(2, len(technical["architecture"]["evidence"]))
self.assertEqual([], user["architecture"]["evidence"])
self.assertEqual(["Example limit."], user["architecture"]["known_limits"])
def test_topics_are_filtered_by_installed_or_selected_version(self) -> None: def test_topics_are_filtered_by_installed_or_selected_version(self) -> None:
registry = PlatformRegistry() registry = PlatformRegistry()
registry.register(ModuleManifest( registry.register(ModuleManifest(
+42
View File
@@ -1,5 +1,42 @@
import { apiFetch, type ApiSettings } from "@govoplan/core-webui"; import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
export type DocsModuleArchitecture = {
contract_version: string;
layer: string;
kind: string;
maturity: string;
evidence: Array<{ kind: string; reference: string; summary: string }>;
known_limits: string[];
supported_authority_modes: string[];
owned_concepts: string[];
non_owned_concepts: string[];
reference_packages: string[];
target_tested_providers: string[];
documentation: Record<string, string[]>;
};
export type DocsExternalProvider = {
id: string;
module_id: string;
label: string;
maturity: string;
operations: string[];
authority_modes: string[];
behavior?: Record<string, unknown>;
known_outage_behavior?: string | null;
runtime_state?: {
configured?: boolean;
active?: boolean;
authority_mode?: string | null;
authority_modes?: string[];
health?: string;
freshness?: string;
conflict?: string;
recovery?: string;
observed_at?: string;
} | null;
};
export type DocsModule = { export type DocsModule = {
id: string; id: string;
name: string; name: string;
@@ -16,6 +53,9 @@ export type DocsModule = {
capabilities: string[]; capabilities: string[];
documentation_count: number; documentation_count: number;
documentation_provider_count: number; documentation_provider_count: number;
architecture?: DocsModuleArchitecture | null;
external_provider_count: number;
external_providers: DocsExternalProvider[];
}; };
export type DocsRoute = { export type DocsRoute = {
@@ -168,6 +208,8 @@ export type DocsContext = {
}; };
summary: { summary: {
module_count: number; module_count: number;
architecture_declared_module_count: number;
external_provider_count: number;
visible_route_count: number; visible_route_count: number;
available_route_count: number; available_route_count: number;
permission_count: number; permission_count: number;
+2
View File
@@ -969,6 +969,8 @@ function ModuleTable({ modules }: { modules: DocsModule[] }) {
if (!modules.length) return <p className="muted">i18n:govoplan-docs.no_configured_modules_found.f6f9ce24</p>; if (!modules.length) return <p className="muted">i18n:govoplan-docs.no_configured_modules_found.f6f9ce24</p>;
const columns: DataGridColumn<DocsModule>[] = [ const columns: DataGridColumn<DocsModule>[] = [
{ id: "module", header: "i18n:govoplan-docs.module.b8ff0289", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sortable: true, filterable: true, value: (module) => `${module.name} ${module.id} ${module.version}`, render: (module) => <div><strong>{module.name}</strong><span className="muted block">{module.id} {module.version}</span></div> }, { id: "module", header: "i18n:govoplan-docs.module.b8ff0289", width: "minmax(220px, 1fr)", minWidth: 190, resizable: true, sortable: true, filterable: true, value: (module) => `${module.name} ${module.id} ${module.version}`, render: (module) => <div><strong>{module.name}</strong><span className="muted block">{module.id} {module.version}</span></div> },
{ id: "architecture", header: "i18n:govoplan-docs.architecture.4ca303a3", width: "minmax(210px, .9fr)", minWidth: 190, resizable: true, sortable: true, filterable: true, value: (module) => module.architecture ? `${module.architecture.layer} ${module.architecture.kind} ${module.architecture.maturity}` : "undeclared", render: (module) => module.architecture ? <div><strong>{module.architecture.maturity}</strong><span className="muted block">{module.architecture.layer} · {module.architecture.kind}</span>{module.architecture.known_limits.length ? <span className="muted block">{module.architecture.known_limits.length} i18n:govoplan-docs.known_limits.31871a6b</span> : null}</div> : <span className="muted">i18n:govoplan-docs.staged_declaration_pending.7ce9c1a1</span> },
{ id: "authority", header: "i18n:govoplan-docs.source_authority.0a863835", width: "minmax(260px, 1fr)", minWidth: 230, resizable: true, filterable: true, value: (module) => `${module.architecture?.supported_authority_modes.join(" ") ?? ""} ${module.external_provider_count} ${module.external_providers.map((provider) => `${provider.runtime_state?.health ?? "unobserved"} ${provider.runtime_state?.freshness ?? ""} ${provider.runtime_state?.conflict ?? ""} ${provider.runtime_state?.recovery ?? ""}`).join(" ")}`, render: (module) => <div>{module.architecture?.supported_authority_modes.length ? module.architecture.supported_authority_modes.join(", ") : "-"}<span className="muted block">{module.external_provider_count} i18n:govoplan-docs.external_providers.d618fc54</span>{module.external_providers.map((provider) => <span className="muted block" key={provider.id}>{provider.label}: {provider.runtime_state ? `${provider.runtime_state.health ?? "unknown"} · ${provider.runtime_state.freshness ?? "unknown"} · ${provider.runtime_state.conflict ?? "unknown"} · ${provider.runtime_state.recovery ?? "unknown"}` : "unobserved"}</span>)}</div> },
{ id: "routes", header: "i18n:govoplan-docs.routes.03730e58", width: 170, sortable: true, value: (module) => module.route_count, render: (module) => <>{module.route_count} i18n:govoplan-docs.route.200e2a66 {module.nav_count} nav</> }, { id: "routes", header: "i18n:govoplan-docs.routes.03730e58", width: 170, sortable: true, value: (module) => module.route_count, render: (module) => <>{module.route_count} i18n:govoplan-docs.route.200e2a66 {module.nav_count} nav</> },
{ id: "permissions", header: "i18n:govoplan-docs.permissions.d06d5557", width: 130, sortable: true, value: (module) => module.permission_count }, { id: "permissions", header: "i18n:govoplan-docs.permissions.d06d5557", width: 130, sortable: true, value: (module) => module.permission_count },
{ id: "frontend", header: "i18n:govoplan-docs.frontend.152d1cf2", width: "minmax(180px, .8fr)", minWidth: 160, resizable: true, sortable: true, filterable: true, value: (module) => module.frontend_package || "-", render: (module) => module.frontend_package || "-" }, { id: "frontend", header: "i18n:govoplan-docs.frontend.152d1cf2", width: "minmax(180px, .8fr)", minWidth: 160, resizable: true, sortable: true, filterable: true, value: (module) => module.frontend_package || "-", render: (module) => module.frontend_package || "-" },
+10
View File
@@ -9,6 +9,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.advanced.203a8d6b": "Advanced", "i18n:govoplan-docs.advanced.203a8d6b": "Advanced",
"i18n:govoplan-docs.available_documentation.db8bcc42": "Available documentation", "i18n:govoplan-docs.available_documentation.db8bcc42": "Available documentation",
"i18n:govoplan-docs.available_routes.c2635868": "Available routes", "i18n:govoplan-docs.available_routes.c2635868": "Available routes",
"i18n:govoplan-docs.architecture.4ca303a3": "Architecture",
"i18n:govoplan-docs.basics.5fcebeef": "Basics", "i18n:govoplan-docs.basics.5fcebeef": "Basics",
"i18n:govoplan-docs.capabilities.ca09c54b": "Capabilities", "i18n:govoplan-docs.capabilities.ca09c54b": "Capabilities",
"i18n:govoplan-docs.common_tasks.9f825c48": "Common tasks", "i18n:govoplan-docs.common_tasks.9f825c48": "Common tasks",
@@ -25,10 +26,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.evidence.7ea014de": "Evidence", "i18n:govoplan-docs.evidence.7ea014de": "Evidence",
"i18n:govoplan-docs.field.7558c082": "Field", "i18n:govoplan-docs.field.7558c082": "Field",
"i18n:govoplan-docs.frontend.152d1cf2": "Frontend", "i18n:govoplan-docs.frontend.152d1cf2": "Frontend",
"i18n:govoplan-docs.external_providers.d618fc54": "external providers",
"i18n:govoplan-docs.granted_permissions.0a232e78": "Granted permissions", "i18n:govoplan-docs.granted_permissions.0a232e78": "Granted permissions",
"i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Guidance for the functions available in this installation.", "i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Guidance for the functions available in this installation.",
"i18n:govoplan-docs.help_center.f3f3a34b": "Help Center", "i18n:govoplan-docs.help_center.f3f3a34b": "Help Center",
"i18n:govoplan-docs.loading_documentation_context.1c091645": "Loading documentation context...", "i18n:govoplan-docs.loading_documentation_context.1c091645": "Loading documentation context...",
"i18n:govoplan-docs.known_limits.31871a6b": "known limits",
"i18n:govoplan-docs.meaning.584d8aa0": "Meaning", "i18n:govoplan-docs.meaning.584d8aa0": "Meaning",
"i18n:govoplan-docs.module.b8ff0289": "Module", "i18n:govoplan-docs.module.b8ff0289": "Module",
"i18n:govoplan-docs.modules.04e9462c": "Modules", "i18n:govoplan-docs.modules.04e9462c": "Modules",
@@ -74,6 +77,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.screen.c4878ec4": "Screen", "i18n:govoplan-docs.screen.c4878ec4": "Screen",
"i18n:govoplan-docs.section.5e498158": "Section", "i18n:govoplan-docs.section.5e498158": "Section",
"i18n:govoplan-docs.source.6da13add": "Source", "i18n:govoplan-docs.source.6da13add": "Source",
"i18n:govoplan-docs.source_authority.0a863835": "Source authority",
"i18n:govoplan-docs.source_details.6dc79c75": "Source details", "i18n:govoplan-docs.source_details.6dc79c75": "Source details",
"i18n:govoplan-docs.inspect_source.bcc1739d": "Inspect source", "i18n:govoplan-docs.inspect_source.bcc1739d": "Inspect source",
"i18n:govoplan-docs.inspection.83dc17ca": "Inspection", "i18n:govoplan-docs.inspection.83dc17ca": "Inspection",
@@ -81,6 +85,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.visibility.80ab5798": "Visibility", "i18n:govoplan-docs.visibility.80ab5798": "Visibility",
"i18n:govoplan-docs.close.87b84f71": "Close", "i18n:govoplan-docs.close.87b84f71": "Close",
"i18n:govoplan-docs.status.bae7d5be": "Status", "i18n:govoplan-docs.status.bae7d5be": "Status",
"i18n:govoplan-docs.staged_declaration_pending.7ce9c1a1": "Staged declaration pending",
"i18n:govoplan-docs.steps.6041435e": "Steps", "i18n:govoplan-docs.steps.6041435e": "Steps",
"i18n:govoplan-docs.summary.d6b9936d": "Summary", "i18n:govoplan-docs.summary.d6b9936d": "Summary",
"i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technical documentation for the modules and configuration active in this installation.", "i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technical documentation for the modules and configuration active in this installation.",
@@ -106,6 +111,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.advanced.203a8d6b": "Fortgeschritten", "i18n:govoplan-docs.advanced.203a8d6b": "Fortgeschritten",
"i18n:govoplan-docs.available_documentation.db8bcc42": "Verfügbare Dokumentation", "i18n:govoplan-docs.available_documentation.db8bcc42": "Verfügbare Dokumentation",
"i18n:govoplan-docs.available_routes.c2635868": "Verfügbare Routen", "i18n:govoplan-docs.available_routes.c2635868": "Verfügbare Routen",
"i18n:govoplan-docs.architecture.4ca303a3": "Architektur",
"i18n:govoplan-docs.basics.5fcebeef": "Grundlagen", "i18n:govoplan-docs.basics.5fcebeef": "Grundlagen",
"i18n:govoplan-docs.capabilities.ca09c54b": "Fähigkeiten", "i18n:govoplan-docs.capabilities.ca09c54b": "Fähigkeiten",
"i18n:govoplan-docs.common_tasks.9f825c48": "Häufige Aufgaben", "i18n:govoplan-docs.common_tasks.9f825c48": "Häufige Aufgaben",
@@ -122,10 +128,12 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.evidence.7ea014de": "Evidence", "i18n:govoplan-docs.evidence.7ea014de": "Evidence",
"i18n:govoplan-docs.field.7558c082": "Feld", "i18n:govoplan-docs.field.7558c082": "Feld",
"i18n:govoplan-docs.frontend.152d1cf2": "Frontend", "i18n:govoplan-docs.frontend.152d1cf2": "Frontend",
"i18n:govoplan-docs.external_providers.d618fc54": "externe Anbieter",
"i18n:govoplan-docs.granted_permissions.0a232e78": "Gewährte Berechtigungen", "i18n:govoplan-docs.granted_permissions.0a232e78": "Gewährte Berechtigungen",
"i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Anleitung für die in dieser Installation verfügbaren Funktionen.", "i18n:govoplan-docs.guidance_for_the_functions_available_in_this_ins.723b7cad": "Anleitung für die in dieser Installation verfügbaren Funktionen.",
"i18n:govoplan-docs.help_center.f3f3a34b": "Hilfezentrum", "i18n:govoplan-docs.help_center.f3f3a34b": "Hilfezentrum",
"i18n:govoplan-docs.loading_documentation_context.1c091645": "Dokumentationskontext wird geladen...", "i18n:govoplan-docs.loading_documentation_context.1c091645": "Dokumentationskontext wird geladen...",
"i18n:govoplan-docs.known_limits.31871a6b": "bekannte Einschränkungen",
"i18n:govoplan-docs.meaning.584d8aa0": "Bedeutung", "i18n:govoplan-docs.meaning.584d8aa0": "Bedeutung",
"i18n:govoplan-docs.module.b8ff0289": "Modul", "i18n:govoplan-docs.module.b8ff0289": "Modul",
"i18n:govoplan-docs.modules.04e9462c": "Module", "i18n:govoplan-docs.modules.04e9462c": "Module",
@@ -171,6 +179,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.screen.c4878ec4": "Ansicht", "i18n:govoplan-docs.screen.c4878ec4": "Ansicht",
"i18n:govoplan-docs.section.5e498158": "Bereich", "i18n:govoplan-docs.section.5e498158": "Bereich",
"i18n:govoplan-docs.source.6da13add": "Quelle", "i18n:govoplan-docs.source.6da13add": "Quelle",
"i18n:govoplan-docs.source_authority.0a863835": "Quellenhoheit",
"i18n:govoplan-docs.source_details.6dc79c75": "Quelldetails", "i18n:govoplan-docs.source_details.6dc79c75": "Quelldetails",
"i18n:govoplan-docs.inspect_source.bcc1739d": "Quelle anzeigen", "i18n:govoplan-docs.inspect_source.bcc1739d": "Quelle anzeigen",
"i18n:govoplan-docs.inspection.83dc17ca": "Prüfdaten", "i18n:govoplan-docs.inspection.83dc17ca": "Prüfdaten",
@@ -178,6 +187,7 @@ export const generatedTranslations: PlatformTranslations = {
"i18n:govoplan-docs.visibility.80ab5798": "Sichtbarkeit", "i18n:govoplan-docs.visibility.80ab5798": "Sichtbarkeit",
"i18n:govoplan-docs.close.87b84f71": "Schließen", "i18n:govoplan-docs.close.87b84f71": "Schließen",
"i18n:govoplan-docs.status.bae7d5be": "Status", "i18n:govoplan-docs.status.bae7d5be": "Status",
"i18n:govoplan-docs.staged_declaration_pending.7ce9c1a1": "Deklaration steht noch aus",
"i18n:govoplan-docs.steps.6041435e": "Schritte", "i18n:govoplan-docs.steps.6041435e": "Schritte",
"i18n:govoplan-docs.summary.d6b9936d": "Zusammenfassung", "i18n:govoplan-docs.summary.d6b9936d": "Zusammenfassung",
"i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technische Dokumentation für die in dieser Installation aktiven Module und Konfiguration.", "i18n:govoplan-docs.technical_documentation_for_the_modules_and_conf.267e739e": "Technische Dokumentation für die in dieser Installation aktiven Module und Konfiguration.",