Compare commits
31
Commits
f407419d25
...
v0.1.34
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23bfe5e2f8 | ||
|
|
cf2f7f6890 | ||
|
|
23b601bc0d | ||
|
|
99c52c2153 | ||
|
|
ca68d98806 | ||
|
|
79c4cb067a | ||
|
|
cd498dc1d8 | ||
|
|
e07b3487e3 | ||
|
|
72279de2c0 | ||
|
|
88543ab115 | ||
|
|
81fe0f4680 | ||
|
|
fe784cc562 | ||
|
|
9fe7ad2cb4 | ||
|
|
f1eebd849c | ||
|
|
4eb90079d5 | ||
|
|
628714804b | ||
|
|
ff9fa37a88 | ||
|
|
4c7552f0dd | ||
|
|
a20e02291d | ||
|
|
b6452c6f53 | ||
|
|
75103d49af | ||
|
|
a60b8b0752 | ||
|
|
7f2f896a0f | ||
|
|
60e04a324c | ||
|
|
6a74e53a1c | ||
|
|
6517b6ac27 | ||
|
|
26a66814b4 | ||
|
|
d08f9f0f2d | ||
|
|
47c90400af | ||
|
|
5d4535f7b5 | ||
|
|
83ccb7f198 |
@@ -21,6 +21,8 @@ CELERY_ENABLED=true
|
|||||||
REDIS_URL=redis://127.0.0.1:6379/0
|
REDIS_URL=redis://127.0.0.1:6379/0
|
||||||
CELERY_QUEUES=send_email,append_sent,notifications,calendar,dataflow,events,default
|
CELERY_QUEUES=send_email,append_sent,notifications,calendar,dataflow,events,default
|
||||||
CALENDAR_OUTBOX_TERMINAL_RETENTION_DAYS=90
|
CALENDAR_OUTBOX_TERMINAL_RETENTION_DAYS=90
|
||||||
|
SCHEDULING_PUBLIC_SELF_ENROLLMENT_ENABLED=true
|
||||||
|
SCHEDULING_PUBLIC_SELF_ENROLLMENT_MAX_CAPACITY=10000
|
||||||
|
|
||||||
GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS=false
|
GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS=false
|
||||||
GOVOPLAN_CONNECTOR_MAX_STRUCTURED_RESPONSE_BYTES=16777216
|
GOVOPLAN_CONNECTOR_MAX_STRUCTURED_RESPONSE_BYTES=16777216
|
||||||
|
|||||||
+9
-1
@@ -82,7 +82,8 @@ Pinned evidence is retained under `evidence/`; completed reviews and migration
|
|||||||
inventories are under `archive/`. They explain or prove a dated state and must
|
inventories are under `archive/`. They explain or prove a dated state and must
|
||||||
not be read as current product status.
|
not be read as current product status.
|
||||||
|
|
||||||
- [Campaign capability and infrastructure fit, 2026-07-22](evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
- [Generated Campaign capability and infrastructure fit, 2026-07-22](evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||||
|
- [Supporting narrative for the 2026-07-22 assessment](evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
||||||
- [Interface surface inventory, 2026-08-03](evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
- [Interface surface inventory, 2026-08-03](evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
||||||
- [Strategic review, 2026-08-05](archive/2026-08/STRATEGIC_REVIEW_2026-08-05.md)
|
- [Strategic review, 2026-08-05](archive/2026-08/STRATEGIC_REVIEW_2026-08-05.md)
|
||||||
- [Meta repository scan, 2026-07-13](archive/2026-07/META_REPO_SCAN.md)
|
- [Meta repository scan, 2026-07-13](archive/2026-07/META_REPO_SCAN.md)
|
||||||
@@ -93,6 +94,13 @@ evidence inputs, and project configuration. Their paths are intentionally
|
|||||||
stable because tools and published schema identifiers consume them; they are
|
stable because tools and published schema identifiers consume them; they are
|
||||||
not additional reading-list entries.
|
not additional reading-list entries.
|
||||||
|
|
||||||
|
Regenerate and verify the human fit report from its JSON input with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./.venv/bin/python tools/assessments/generate-capability-fit-report.py
|
||||||
|
./.venv/bin/python tools/assessments/generate-capability-fit-report.py --check
|
||||||
|
```
|
||||||
|
|
||||||
## Maintenance Rules
|
## Maintenance Rules
|
||||||
|
|
||||||
1. Gitea issues are the only live source for work state, priority, and owner.
|
1. Gitea issues are the only live source for work state, priority, and owner.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Read this together with:
|
|||||||
- [Reference Journey Program](../strategy/REFERENCE_JOURNEY_PROGRAM.md)
|
- [Reference Journey Program](../strategy/REFERENCE_JOURNEY_PROGRAM.md)
|
||||||
- [Module Contracts and Install Boundaries](../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
- [Module Contracts and Install Boundaries](../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||||
- [Datasource and Definition Graph Architecture](DATASOURCE_AND_DEFINITION_GRAPH_ARCHITECTURE.md)
|
- [Datasource and Definition Graph Architecture](DATASOURCE_AND_DEFINITION_GRAPH_ARCHITECTURE.md)
|
||||||
- [Capability and Infrastructure Fit](../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
- [Generated Capability and Infrastructure Fit](../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||||
- [Core Module Architecture](../../../govoplan-core/docs/MODULE_ARCHITECTURE.md)
|
- [Core Module Architecture](../../../govoplan-core/docs/MODULE_ARCHITECTURE.md)
|
||||||
- [Core External References and Integration Maturity](../../../govoplan-core/docs/EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md)
|
- [Core External References and Integration Maturity](../../../govoplan-core/docs/EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md)
|
||||||
- [Core Action, Effect, and Automation Layer](../../../govoplan-core/docs/ACTION_EFFECT_AUTOMATION_LAYER.md)
|
- [Core Action, Effect, and Automation Layer](../../../govoplan-core/docs/ACTION_EFFECT_AUTOMATION_LAYER.md)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "./capability-fit.schema.json",
|
"$schema": "./capability-fit.schema.json",
|
||||||
"schema_version": "0.1.0",
|
"schema_version": "0.2.0",
|
||||||
"assessment_id": "campaign-reference-2026-07-22",
|
"assessment_id": "campaign-reference-2026-07-22",
|
||||||
"assessed_at": "2026-07-22",
|
"assessed_at": "2026-07-22",
|
||||||
"scope": {
|
"scope": {
|
||||||
@@ -13,16 +13,30 @@
|
|||||||
"Workflow and workflow-driven user stories"
|
"Workflow and workflow-driven user stories"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"facts": [
|
||||||
|
"The assessment is pinned to signed stable catalog sequence 202607220843 and the exact module commits listed below.",
|
||||||
|
"The Campaign authoring, validation, build, mock-delivery, managed-file, local-access, and local-audit paths have direct test or contract evidence.",
|
||||||
|
"The production-like development profile runs PostgreSQL and Redis in containers while application processes use editable source trees.",
|
||||||
|
"No installed-target, external-provider, reference-readiness, recovery, or production-approval evidence bundle is attached to this assessment."
|
||||||
|
],
|
||||||
|
"decisions": [
|
||||||
|
"Use Campaign as the first reference journey and flagship pilot scenario.",
|
||||||
|
"Keep Workflow and workflow-driven user stories planned and explicitly postponed for this assessment.",
|
||||||
|
"Use local GovOPlaN accounts for the bounded pilot; do not claim federated identity support.",
|
||||||
|
"Do not approve small production until installed-artifact, target mail, monitoring, backup/restore, and recovery proof checks pass."
|
||||||
|
],
|
||||||
"release": {
|
"release": {
|
||||||
"kind": "tagged_release",
|
"kind": "tagged_release",
|
||||||
"ref": "stable-catalog-202607220843",
|
"ref": "stable-catalog-202607220843",
|
||||||
"meta_commit": "5447299289a1",
|
"meta_commit": "5447299289a1",
|
||||||
"reproducible": true,
|
"reproducible": true,
|
||||||
"configuration_packages": [],
|
"configuration_packages": [
|
||||||
|
"none: environment-profile basis only"
|
||||||
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
"The live stable catalog has a valid Ed25519 signature trusted through release-key-1.",
|
"The live stable catalog has a valid Ed25519 signature trusted through release-key-1.",
|
||||||
"Core v0.1.13 and Campaign v0.1.10 are tagged and package-integrated; this is not target-environment or production approval.",
|
"Core v0.1.13 and Campaign v0.1.10 are tagged and package-integrated; this is not target-environment or production approval.",
|
||||||
"No configuration revision or configuration package is pinned yet."
|
"The absence of a configuration package is pinned explicitly as an environment-profile-only basis; this remains a promotion gap."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"composition": [
|
"composition": [
|
||||||
@@ -188,6 +202,125 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"scenarios": [
|
||||||
|
{
|
||||||
|
"id": "campaign-pilot",
|
||||||
|
"label": "Controlled Campaign pilot",
|
||||||
|
"status": "partial",
|
||||||
|
"recommendation": "Proceed with a bounded internal pilot after its provider, privacy, workload, and recovery proof checks are assigned and passed.",
|
||||||
|
"composition": [
|
||||||
|
"core",
|
||||||
|
"tenancy",
|
||||||
|
"organizations",
|
||||||
|
"identity",
|
||||||
|
"access",
|
||||||
|
"admin",
|
||||||
|
"dashboard",
|
||||||
|
"policy",
|
||||||
|
"audit",
|
||||||
|
"campaigns",
|
||||||
|
"files",
|
||||||
|
"mail",
|
||||||
|
"docs",
|
||||||
|
"ops"
|
||||||
|
],
|
||||||
|
"topology": [
|
||||||
|
"One supervised GovOPlaN API process and one immutable built WebUI behind deployment-owned TLS termination",
|
||||||
|
"One PostgreSQL database and a durable single-node or shared managed-file path",
|
||||||
|
"One persistent private Redis broker and one supervised Celery worker when asynchronous delivery is enabled",
|
||||||
|
"One dedicated non-production SMTP/IMAP account with a restricted safe-recipient policy",
|
||||||
|
"External health checks, centralized logs, protected secret injection, and coordinated backup storage"
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
"Use one internal tenant or office and controlled operators.",
|
||||||
|
"Keep recipient volume non-critical until measured.",
|
||||||
|
"Enable Addresses only when reusable recipient lists or CardDAV are explicitly in scope.",
|
||||||
|
"Do not enable or claim Workflow from this assessment."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "small-production-candidate",
|
||||||
|
"label": "Small-production candidate",
|
||||||
|
"status": "partial",
|
||||||
|
"recommendation": "Do not approve production until every listed operational gate has target evidence and the residual risks have named owners.",
|
||||||
|
"composition": [
|
||||||
|
"core",
|
||||||
|
"tenancy",
|
||||||
|
"organizations",
|
||||||
|
"identity",
|
||||||
|
"access",
|
||||||
|
"admin",
|
||||||
|
"dashboard",
|
||||||
|
"policy",
|
||||||
|
"audit",
|
||||||
|
"campaigns",
|
||||||
|
"files",
|
||||||
|
"mail",
|
||||||
|
"docs",
|
||||||
|
"ops"
|
||||||
|
],
|
||||||
|
"topology": [
|
||||||
|
"Immutable separately supervised WebUI, API, and worker artifacts behind monitored reverse-proxy TLS",
|
||||||
|
"Dedicated or managed PostgreSQL with measured coordinated backup and isolated restore",
|
||||||
|
"Persistent authenticated Redis with queue-age, queue-depth, and worker-health alerts",
|
||||||
|
"Durable shared or S3-compatible object storage with versioning, lifecycle, and restore evidence",
|
||||||
|
"Target-native secret management, centralized monitoring/logging/audit export, and an exercised incident and disaster-recovery procedure"
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
"Pin and promote a configuration package instead of relying on an environment-only basis.",
|
||||||
|
"Pass installed-release, target SMTP/IMAP, accessibility, privacy, security, operations, and recovery evidence gates.",
|
||||||
|
"Agree availability, RPO, RTO, retention, support, and procurement requirements.",
|
||||||
|
"Run only one scheduler unless distributed leadership or locking is proved."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"functional_context": {
|
||||||
|
"required_modules": [
|
||||||
|
"core",
|
||||||
|
"tenancy",
|
||||||
|
"organizations",
|
||||||
|
"identity",
|
||||||
|
"access",
|
||||||
|
"admin",
|
||||||
|
"dashboard",
|
||||||
|
"policy",
|
||||||
|
"audit",
|
||||||
|
"campaigns",
|
||||||
|
"files",
|
||||||
|
"mail",
|
||||||
|
"docs",
|
||||||
|
"ops"
|
||||||
|
],
|
||||||
|
"optional_modules": [
|
||||||
|
"addresses"
|
||||||
|
],
|
||||||
|
"external_systems": [
|
||||||
|
"Deployment-owned reverse proxy and TLS certificate lifecycle",
|
||||||
|
"Target SMTP/IMAP service and its DNS, certificate, throttling, bounce, and reply policies",
|
||||||
|
"Target-native secret store, monitoring/logging platform, backup storage, and incident-response process"
|
||||||
|
],
|
||||||
|
"missing_contracts": [
|
||||||
|
"End-to-end federated identity provider and lifecycle contract",
|
||||||
|
"Target monitoring, alert delivery, and central audit/SIEM acceptance contract",
|
||||||
|
"Production configuration-package promotion and approval evidence"
|
||||||
|
],
|
||||||
|
"policy_decisions": [
|
||||||
|
"Recipient allow-list, permitted sender, attachment, retention, and external-disclosure policy",
|
||||||
|
"Identity, MFA, break-glass, service-account, and joiner/mover/leaver policy",
|
||||||
|
"Availability, RPO, RTO, support, procurement, and residual-risk ownership"
|
||||||
|
],
|
||||||
|
"manual_workarounds": [
|
||||||
|
"Use controlled local accounts while federation remains outside the verified slice",
|
||||||
|
"Use one supervised scheduler where periodic work is unavoidable",
|
||||||
|
"Keep provider reconciliation and production promotion under explicit operator review"
|
||||||
|
],
|
||||||
|
"blockers": [
|
||||||
|
"No promoted configuration package is pinned",
|
||||||
|
"No installed-target or target SMTP/IMAP proof is attached",
|
||||||
|
"No coherent target backup/restore or disaster-recovery drill with measured RPO/RTO is attached",
|
||||||
|
"No target privacy, security, accessibility, operations, or production-approval evidence is attached"
|
||||||
|
]
|
||||||
|
},
|
||||||
"questionnaire": {
|
"questionnaire": {
|
||||||
"scope_outcomes": [
|
"scope_outcomes": [
|
||||||
{
|
{
|
||||||
@@ -203,6 +336,20 @@
|
|||||||
"state": "answered",
|
"state": "answered",
|
||||||
"answer": "No; Workflow is planned and explicitly postponed.",
|
"answer": "No; Workflow is planned and explicitly postponed.",
|
||||||
"evidence": []
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "scope.users_tenants_organizations",
|
||||||
|
"question": "Which users, roles, tenants, organization units, and delegated functions participate?",
|
||||||
|
"state": "assumed",
|
||||||
|
"answer": "One internal tenant or office with controlled Campaign operators; detailed organization and delegation shape remains target-specific.",
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "outcome.acceptance",
|
||||||
|
"question": "What constitutes pilot success and production acceptance?",
|
||||||
|
"state": "answered",
|
||||||
|
"answer": "Pilot success requires the bounded Campaign journey and proof checks; production additionally requires installed-artifact, provider, privacy, security, operations, recovery, and approval evidence.",
|
||||||
|
"evidence": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"data_policy": [
|
"data_policy": [
|
||||||
@@ -219,6 +366,13 @@
|
|||||||
"state": "not_assessed",
|
"state": "not_assessed",
|
||||||
"answer": null,
|
"answer": null,
|
||||||
"evidence": []
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "data.privacy_security_disclosure",
|
||||||
|
"question": "Which privacy, security, residency, minimization, access, and external-disclosure constraints apply?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"identity_integrations": [
|
"identity_integrations": [
|
||||||
@@ -235,22 +389,50 @@
|
|||||||
"state": "not_assessed",
|
"state": "not_assessed",
|
||||||
"answer": null,
|
"answer": null,
|
||||||
"evidence": []
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "identity.protocols_lifecycle",
|
||||||
|
"question": "Which identity protocols, MFA, joiner/mover/leaver, service-account, and break-glass rules are mandatory?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "integration.protocols_network",
|
||||||
|
"question": "Which connector protocols, versions, directions, authentication, certificate, rate-limit, egress, and degraded-mode requirements apply?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"workload_growth": [
|
"workload_growth": [
|
||||||
{
|
{
|
||||||
"id": "workload.campaign",
|
"id": "workload.campaign_volume_peaks",
|
||||||
"question": "What are Campaign frequency, recipients per Campaign, send window, import size and attachment volume?",
|
"question": "What are Campaign frequency, recipients per Campaign, send window, import size and attachment volume?",
|
||||||
"state": "not_assessed",
|
"state": "not_assessed",
|
||||||
"answer": null,
|
"answer": null,
|
||||||
"evidence": []
|
"evidence": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "workload.platform",
|
"id": "workload.tenants_users_concurrency",
|
||||||
|
"question": "What are tenant, named-user, active-user, concurrent-user, and peak-request assumptions?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "workload.files_jobs_audit_growth_retention",
|
||||||
"question": "What are tenant, user, concurrency, file, database, queue and audit growth assumptions?",
|
"question": "What are tenant, user, concurrency, file, database, queue and audit growth assumptions?",
|
||||||
"state": "not_assessed",
|
"state": "not_assessed",
|
||||||
"answer": null,
|
"answer": null,
|
||||||
"evidence": []
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "workload.connector_traffic_batches",
|
||||||
|
"question": "What connector traffic, scheduled-job, batch, queue-depth, queue-age, and external-rate-limit peaks apply?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"availability_operations": [
|
"availability_operations": [
|
||||||
@@ -267,6 +449,13 @@
|
|||||||
"state": "not_assessed",
|
"state": "not_assessed",
|
||||||
"answer": null,
|
"answer": null,
|
||||||
"evidence": []
|
"evidence": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hosting.network_constraints",
|
||||||
|
"question": "Which hosting, network-zone, egress, proxy, DNS, NTP, certificate-authority, residency, or disconnected-operation constraints apply?",
|
||||||
|
"state": "not_assessed",
|
||||||
|
"answer": null,
|
||||||
|
"evidence": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"procurement_decisions": [
|
"procurement_decisions": [
|
||||||
@@ -754,6 +943,63 @@
|
|||||||
"recommendation": "Use target-native secret injection and document rotation/recovery.",
|
"recommendation": "Use target-native secret injection and document rotation/recovery.",
|
||||||
"proof_check": "Rotate a non-production credential and recover from a protected backup."
|
"proof_check": "Rotate a non-production credential and recover from a protected backup."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "identity.access",
|
||||||
|
"requirement": "Authenticate users and enforce tenant-scoped authorization through the selected identity mode.",
|
||||||
|
"status": "verified",
|
||||||
|
"evidence": [
|
||||||
|
{
|
||||||
|
"kind": "test",
|
||||||
|
"scope": "committed_source",
|
||||||
|
"locator": "govoplan-access/tests/test_auth_dependencies.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "test",
|
||||||
|
"scope": "committed_source",
|
||||||
|
"locator": "govoplan-core/tests/test_api_smoke.py#cookie-session-csrf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
"The bounded pilot accepts local GovOPlaN accounts."
|
||||||
|
],
|
||||||
|
"gaps": [
|
||||||
|
"Target MFA, federation, provisioning, and joiner/mover/leaver requirements are not assessed."
|
||||||
|
],
|
||||||
|
"risks": [
|
||||||
|
"A local-only identity topology may not satisfy institutional production policy."
|
||||||
|
],
|
||||||
|
"recommendation": "Use controlled local pilot accounts and assess the mandatory production identity topology separately.",
|
||||||
|
"proof_check": "Exercise login, role change, account suspension, protected bootstrap, and break-glass recovery in the target."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "connectors.mail",
|
||||||
|
"requirement": "Reach the selected SMTP/IMAP and other external connector endpoints under explicit network and provider policy.",
|
||||||
|
"status": "available_unconfigured",
|
||||||
|
"evidence": [
|
||||||
|
{
|
||||||
|
"kind": "test",
|
||||||
|
"scope": "current_workspace",
|
||||||
|
"locator": "govoplan-mail/tests",
|
||||||
|
"note": "Protocol adapters have direct tests; no target provider was exercised"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "documentation",
|
||||||
|
"scope": "documented_model",
|
||||||
|
"locator": "govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
"The deployment supplies DNS, egress, proxy, CA trust, scoped service accounts, and provider limits."
|
||||||
|
],
|
||||||
|
"gaps": [
|
||||||
|
"No target endpoint, TLS chain, throttling, sender policy, bounce/reply path, or disclosure agreement is assessed."
|
||||||
|
],
|
||||||
|
"risks": [
|
||||||
|
"Provider rejection, delay, or ambiguous outcomes can affect delivery and evidence completeness."
|
||||||
|
],
|
||||||
|
"recommendation": "Use a dedicated safe provider account for the pilot and require target interoperability evidence before production.",
|
||||||
|
"proof_check": "Exercise target-like SMTP acceptance, IMAP append, throttling, outage, retry, and reconciliation through the approved network path."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "operations.monitoring",
|
"id": "operations.monitoring",
|
||||||
"requirement": "Detect API, database, worker, queue, storage and delivery degradation.",
|
"requirement": "Detect API, database, worker, queue, storage and delivery degradation.",
|
||||||
@@ -780,6 +1026,30 @@
|
|||||||
"recommendation": "Integrate external monitoring before small production.",
|
"recommendation": "Integrate external monitoring before small production.",
|
||||||
"proof_check": "Trigger each readiness/delivery failure and verify an actionable alert."
|
"proof_check": "Trigger each readiness/delivery failure and verify an actionable alert."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "operations.audit",
|
||||||
|
"requirement": "Retain, monitor, review, and where required export security and business audit evidence.",
|
||||||
|
"status": "partial",
|
||||||
|
"evidence": [
|
||||||
|
{
|
||||||
|
"kind": "test",
|
||||||
|
"scope": "current_workspace",
|
||||||
|
"locator": "govoplan-audit/tests",
|
||||||
|
"note": "Local audit persistence and retry behavior are exercised"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
"Local database audit evidence is part of coordinated backup and access review."
|
||||||
|
],
|
||||||
|
"gaps": [
|
||||||
|
"Target retention enforcement, tamper-evident export, SIEM integration, alerting, and privileged review are not verified."
|
||||||
|
],
|
||||||
|
"risks": [
|
||||||
|
"Local evidence alone may not meet institutional security, records, or incident-response requirements."
|
||||||
|
],
|
||||||
|
"recommendation": "Define the target audit retention, export, monitoring, and review controls before production approval.",
|
||||||
|
"proof_check": "Exercise privileged-event review, retention, export failure/retry, and target SIEM or archive ingestion."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "operations.backup_restore",
|
"id": "operations.backup_restore",
|
||||||
"requirement": "Back up and restore database, files, configuration and keys as a coherent service.",
|
"requirement": "Back up and restore database, files, configuration and keys as a coherent service.",
|
||||||
|
|||||||
@@ -9,9 +9,13 @@
|
|||||||
"assessment_id",
|
"assessment_id",
|
||||||
"assessed_at",
|
"assessed_at",
|
||||||
"scope",
|
"scope",
|
||||||
|
"facts",
|
||||||
|
"decisions",
|
||||||
"release",
|
"release",
|
||||||
"composition",
|
"composition",
|
||||||
"deployment_profile",
|
"deployment_profile",
|
||||||
|
"scenarios",
|
||||||
|
"functional_context",
|
||||||
"questionnaire",
|
"questionnaire",
|
||||||
"capabilities",
|
"capabilities",
|
||||||
"infrastructure",
|
"infrastructure",
|
||||||
@@ -28,7 +32,7 @@
|
|||||||
"format": "uri-reference"
|
"format": "uri-reference"
|
||||||
},
|
},
|
||||||
"schema_version": {
|
"schema_version": {
|
||||||
"const": "0.1.0"
|
"const": "0.2.0"
|
||||||
},
|
},
|
||||||
"assessment_id": {
|
"assessment_id": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
@@ -54,6 +58,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"facts": { "$ref": "#/$defs/string_list" },
|
||||||
|
"decisions": { "$ref": "#/$defs/string_list" },
|
||||||
"release": {
|
"release": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -95,6 +101,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"scenarios": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 2,
|
||||||
|
"items": { "$ref": "#/$defs/scenario" }
|
||||||
|
},
|
||||||
|
"functional_context": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"required_modules",
|
||||||
|
"optional_modules",
|
||||||
|
"external_systems",
|
||||||
|
"missing_contracts",
|
||||||
|
"policy_decisions",
|
||||||
|
"manual_workarounds",
|
||||||
|
"blockers"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"required_modules": { "$ref": "#/$defs/string_list" },
|
||||||
|
"optional_modules": { "$ref": "#/$defs/string_list" },
|
||||||
|
"external_systems": { "$ref": "#/$defs/string_list" },
|
||||||
|
"missing_contracts": { "$ref": "#/$defs/string_list" },
|
||||||
|
"policy_decisions": { "$ref": "#/$defs/string_list" },
|
||||||
|
"manual_workarounds": { "$ref": "#/$defs/string_list" },
|
||||||
|
"blockers": { "$ref": "#/$defs/string_list" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"questionnaire": {
|
"questionnaire": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -239,6 +272,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"scenario": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"status",
|
||||||
|
"recommendation",
|
||||||
|
"composition",
|
||||||
|
"topology",
|
||||||
|
"conditions"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": { "$ref": "#/$defs/non_empty_string" },
|
||||||
|
"label": { "$ref": "#/$defs/non_empty_string" },
|
||||||
|
"status": { "$ref": "#/$defs/status" },
|
||||||
|
"recommendation": { "$ref": "#/$defs/non_empty_string" },
|
||||||
|
"composition": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"uniqueItems": true,
|
||||||
|
"items": { "$ref": "#/$defs/non_empty_string" }
|
||||||
|
},
|
||||||
|
"topology": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": { "$ref": "#/$defs/non_empty_string" }
|
||||||
|
},
|
||||||
|
"conditions": { "$ref": "#/$defs/string_list" }
|
||||||
|
}
|
||||||
|
},
|
||||||
"assessed_item": {
|
"assessed_item": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -259,6 +323,7 @@
|
|||||||
"status": { "$ref": "#/$defs/status" },
|
"status": { "$ref": "#/$defs/status" },
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
"items": { "$ref": "#/$defs/evidence" }
|
"items": { "$ref": "#/$defs/evidence" }
|
||||||
},
|
},
|
||||||
"conditions": { "$ref": "#/$defs/string_list" },
|
"conditions": { "$ref": "#/$defs/string_list" },
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
# GovOPlaN Capability and IT-Infrastructure Fit Assessment
|
||||||
|
|
||||||
|
> Generated from [`capability-fit-current.json`](../../capability-fit-current.json).
|
||||||
|
> Edit and validate the machine-readable assessment, then regenerate this file;
|
||||||
|
> do not maintain conclusions independently in Markdown.
|
||||||
|
|
||||||
|
This is an evidence-based fit assessment, not a production approval or
|
||||||
|
security certification. Repository or manifest existence alone never counts
|
||||||
|
as an implemented capability. Unknown target requirements remain explicitly
|
||||||
|
`not_assessed`.
|
||||||
|
|
||||||
|
## Assessment record
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Assessment ID | `campaign-reference-2026-07-22` |
|
||||||
|
| Schema version | `govoplan.fit-assessment/0.2.0` |
|
||||||
|
| Assessed on | 2026-07-22 |
|
||||||
|
| Scope | Campaign-centric internal pilot and small-production candidate |
|
||||||
|
| Release | `stable-catalog-202607220843` (tagged_release) |
|
||||||
|
| Meta commit | `5447299289a1` |
|
||||||
|
| Deployment profile | `production-like-dev` · `partial` |
|
||||||
|
| Configuration packages | `none: environment-profile basis only` |
|
||||||
|
| Canonical input SHA-256 | `5a23f17c5289c5a89d2e92445f2c8b2eef54e3753f1392ebf300aff5508f0bfe` |
|
||||||
|
|
||||||
|
## Controlled status vocabulary
|
||||||
|
|
||||||
|
| Status | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `verified` | Implemented and directly exercised by evidence appropriate to the stated scope. |
|
||||||
|
| `available_unconfigured` | Implemented with supporting evidence, but not configured and exercised in the target. |
|
||||||
|
| `partial` | A useful subset exists, but a material part of the requirement is missing or unproved. |
|
||||||
|
| `scaffold` | Contracts or structure exist, but the end-to-end capability is not usable. |
|
||||||
|
| `external_system` | The deployment or another system must supply the capability. |
|
||||||
|
| `planned` | Only a concept, backlog item, or design direction exists. |
|
||||||
|
| `not_fit` | Evidence shows that the assessed composition cannot meet the requirement. |
|
||||||
|
| `not_assessed` | The requirement or target environment is not sufficiently known. |
|
||||||
|
|
||||||
|
## Scope and reference journeys
|
||||||
|
|
||||||
|
Reference journeys:
|
||||||
|
|
||||||
|
- Internal operator authors, validates, builds, queues, sends and reconciles an email Campaign with managed attachments
|
||||||
|
- Operator inspects delivery and audit evidence
|
||||||
|
|
||||||
|
Explicitly postponed:
|
||||||
|
|
||||||
|
- Workflow and workflow-driven user stories
|
||||||
|
|
||||||
|
## Facts
|
||||||
|
|
||||||
|
- The assessment is pinned to signed stable catalog sequence 202607220843 and the exact module commits listed below.
|
||||||
|
- The Campaign authoring, validation, build, mock-delivery, managed-file, local-access, and local-audit paths have direct test or contract evidence.
|
||||||
|
- The production-like development profile runs PostgreSQL and Redis in containers while application processes use editable source trees.
|
||||||
|
- No installed-target, external-provider, reference-readiness, recovery, or production-approval evidence bundle is attached to this assessment.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Use Campaign as the first reference journey and flagship pilot scenario.
|
||||||
|
- Keep Workflow and workflow-driven user stories planned and explicitly postponed for this assessment.
|
||||||
|
- Use local GovOPlaN accounts for the bounded pilot; do not claim federated identity support.
|
||||||
|
- Do not approve small production until installed-artifact, target mail, monitoring, backup/restore, and recovery proof checks pass.
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- The pilot can use local accounts and one internal tenant or office.
|
||||||
|
- A dedicated non-production SMTP/IMAP account and safe recipients are available.
|
||||||
|
- Pilot load fits one API and one worker until measured otherwise.
|
||||||
|
- Durable local storage is acceptable for the pilot.
|
||||||
|
|
||||||
|
## Unresolved decisions
|
||||||
|
|
||||||
|
- What are the target organization's data classes, legal bases, retention and external-disclosure rules?
|
||||||
|
- Which identity, mail, file, address and monitoring systems are mandatory?
|
||||||
|
- What are Campaign volume, concurrency, growth, availability, RPO and RTO?
|
||||||
|
- Who owns each external runtime component and operational control?
|
||||||
|
- Which accessibility, security, support and procurement constraints are mandatory?
|
||||||
|
|
||||||
|
## Pinned release and composition
|
||||||
|
|
||||||
|
Release reproducible: **yes**.
|
||||||
|
|
||||||
|
Release notes:
|
||||||
|
|
||||||
|
- The live stable catalog has a valid Ed25519 signature trusted through release-key-1.
|
||||||
|
- Core v0.1.13 and Campaign v0.1.10 are tagged and package-integrated; this is not target-environment or production approval.
|
||||||
|
- The absence of a configuration package is pinned explicitly as an environment-profile-only basis; this remains a promotion gap.
|
||||||
|
|
||||||
|
| Module | Repository and commit | Manifest version | Enabled | Role |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `core` | `govoplan-core` @ `d487726f4d2c` | `0.1.13` | yes | API, registry, migrations, sessions, kernel contracts and shared WebUI |
|
||||||
|
| `tenancy` | `govoplan-tenancy` @ `efbec827616b` | `0.1.8` | yes | Tenant context and lifecycle |
|
||||||
|
| `organizations` | `govoplan-organizations` @ `39c081c4fb8f` | `0.1.8` | yes | Organization model |
|
||||||
|
| `identity` | `govoplan-identity` @ `7a1710af896f` | `0.1.8` | yes | Normalized internal identity directory |
|
||||||
|
| `access` | `govoplan-access` @ `f1d64d247e12` | `0.1.11` | yes | Local authentication, sessions, API keys and RBAC |
|
||||||
|
| `admin` | `govoplan-admin` @ `11ecf362a36d` | `0.1.8` | yes | Administration surfaces |
|
||||||
|
| `dashboard` | `govoplan-dashboard` @ `4b960ad37f0d` | `0.1.8` | yes | Module-aware home surface |
|
||||||
|
| `policy` | `govoplan-policy` @ `1063622d311a` | `0.1.9` | yes | Policy explanation and configuration boundary |
|
||||||
|
| `audit` | `govoplan-audit` @ `d3d2c60d7dc1` | `0.1.8` | yes | Database audit records and retrying audit outbox |
|
||||||
|
| `campaigns` | `govoplan-campaign` @ `735e874bd03c` | `0.1.10` | yes | Campaign authoring, build, delivery control and reporting |
|
||||||
|
| `files` | `govoplan-files` @ `2b34f6e30578` | `0.1.9` | yes | Managed files and Campaign attachments |
|
||||||
|
| `mail` | `govoplan-mail` @ `3e2302909022` | `0.1.10` | yes | SMTP and IMAP profiles and transports |
|
||||||
|
| `calendar` | `govoplan-calendar` @ `9bcf41bb1fbb` | `0.1.8` | yes | Optional calendar outside the Campaign pilot minimum |
|
||||||
|
| `docs` | `govoplan-docs` @ `be52b716caed` | `0.1.10` | yes | Configured-system documentation |
|
||||||
|
| `ops` | `govoplan-ops` @ `341773a4ff8a` | `0.1.8` | yes | Readiness and deployment-profile visibility |
|
||||||
|
| `addresses` | `govoplan-addresses` @ `93dddbb8c52a` | `0.1.9` | no | Optional reusable recipient sources and CardDAV |
|
||||||
|
|
||||||
|
## Deployment profile
|
||||||
|
|
||||||
|
Status: `partial`
|
||||||
|
|
||||||
|
PostgreSQL and Redis run in containers while API, WebUI, worker and scheduler run from editable source trees.
|
||||||
|
|
||||||
|
Evidence:
|
||||||
|
|
||||||
|
- configuration/current_workspace: govoplan/dev/production-like/docker-compose.yml
|
||||||
|
- documentation/documented_model: govoplan/dev/production-like/README.md
|
||||||
|
|
||||||
|
## Recommended scenarios
|
||||||
|
|
||||||
|
### Controlled Campaign pilot
|
||||||
|
|
||||||
|
Status: `partial`
|
||||||
|
|
||||||
|
Proceed with a bounded internal pilot after its provider, privacy, workload, and recovery proof checks are assigned and passed.
|
||||||
|
|
||||||
|
Composition: `core`, `tenancy`, `organizations`, `identity`, `access`, `admin`, `dashboard`, `policy`, `audit`, `campaigns`, `files`, `mail`, `docs`, `ops`.
|
||||||
|
|
||||||
|
Topology:
|
||||||
|
|
||||||
|
- One supervised GovOPlaN API process and one immutable built WebUI behind deployment-owned TLS termination
|
||||||
|
- One PostgreSQL database and a durable single-node or shared managed-file path
|
||||||
|
- One persistent private Redis broker and one supervised Celery worker when asynchronous delivery is enabled
|
||||||
|
- One dedicated non-production SMTP/IMAP account with a restricted safe-recipient policy
|
||||||
|
- External health checks, centralized logs, protected secret injection, and coordinated backup storage
|
||||||
|
|
||||||
|
Conditions:
|
||||||
|
|
||||||
|
- Use one internal tenant or office and controlled operators.
|
||||||
|
- Keep recipient volume non-critical until measured.
|
||||||
|
- Enable Addresses only when reusable recipient lists or CardDAV are explicitly in scope.
|
||||||
|
- Do not enable or claim Workflow from this assessment.
|
||||||
|
|
||||||
|
### Small-production candidate
|
||||||
|
|
||||||
|
Status: `partial`
|
||||||
|
|
||||||
|
Do not approve production until every listed operational gate has target evidence and the residual risks have named owners.
|
||||||
|
|
||||||
|
Composition: `core`, `tenancy`, `organizations`, `identity`, `access`, `admin`, `dashboard`, `policy`, `audit`, `campaigns`, `files`, `mail`, `docs`, `ops`.
|
||||||
|
|
||||||
|
Topology:
|
||||||
|
|
||||||
|
- Immutable separately supervised WebUI, API, and worker artifacts behind monitored reverse-proxy TLS
|
||||||
|
- Dedicated or managed PostgreSQL with measured coordinated backup and isolated restore
|
||||||
|
- Persistent authenticated Redis with queue-age, queue-depth, and worker-health alerts
|
||||||
|
- Durable shared or S3-compatible object storage with versioning, lifecycle, and restore evidence
|
||||||
|
- Target-native secret management, centralized monitoring/logging/audit export, and an exercised incident and disaster-recovery procedure
|
||||||
|
|
||||||
|
Conditions:
|
||||||
|
|
||||||
|
- Pin and promote a configuration package instead of relying on an environment-only basis.
|
||||||
|
- Pass installed-release, target SMTP/IMAP, accessibility, privacy, security, operations, and recovery evidence gates.
|
||||||
|
- Agree availability, RPO, RTO, retention, support, and procurement requirements.
|
||||||
|
- Run only one scheduler unless distributed leadership or locking is proved.
|
||||||
|
|
||||||
|
## Functional matrix context
|
||||||
|
|
||||||
|
### Required modules
|
||||||
|
|
||||||
|
- core
|
||||||
|
- tenancy
|
||||||
|
- organizations
|
||||||
|
- identity
|
||||||
|
- access
|
||||||
|
- admin
|
||||||
|
- dashboard
|
||||||
|
- policy
|
||||||
|
- audit
|
||||||
|
- campaigns
|
||||||
|
- files
|
||||||
|
- mail
|
||||||
|
- docs
|
||||||
|
- ops
|
||||||
|
|
||||||
|
### Optional modules
|
||||||
|
|
||||||
|
- addresses
|
||||||
|
|
||||||
|
### External systems and connectors
|
||||||
|
|
||||||
|
- Deployment-owned reverse proxy and TLS certificate lifecycle
|
||||||
|
- Target SMTP/IMAP service and its DNS, certificate, throttling, bounce, and reply policies
|
||||||
|
- Target-native secret store, monitoring/logging platform, backup storage, and incident-response process
|
||||||
|
|
||||||
|
### Missing contracts
|
||||||
|
|
||||||
|
- End-to-end federated identity provider and lifecycle contract
|
||||||
|
- Target monitoring, alert delivery, and central audit/SIEM acceptance contract
|
||||||
|
- Production configuration-package promotion and approval evidence
|
||||||
|
|
||||||
|
### Policy decisions
|
||||||
|
|
||||||
|
- Recipient allow-list, permitted sender, attachment, retention, and external-disclosure policy
|
||||||
|
- Identity, MFA, break-glass, service-account, and joiner/mover/leaver policy
|
||||||
|
- Availability, RPO, RTO, support, procurement, and residual-risk ownership
|
||||||
|
|
||||||
|
### Manual workarounds
|
||||||
|
|
||||||
|
- Use controlled local accounts while federation remains outside the verified slice
|
||||||
|
- Use one supervised scheduler where periodic work is unavoidable
|
||||||
|
- Keep provider reconciliation and production promotion under explicit operator review
|
||||||
|
|
||||||
|
### Blockers
|
||||||
|
|
||||||
|
- No promoted configuration package is pinned
|
||||||
|
- No installed-target or target SMTP/IMAP proof is attached
|
||||||
|
- No coherent target backup/restore or disaster-recovery drill with measured RPO/RTO is attached
|
||||||
|
- No target privacy, security, accessibility, operations, or production-approval evidence is attached
|
||||||
|
|
||||||
|
## Assessment questionnaire
|
||||||
|
|
||||||
|
Every required area remains visible even when its target answer is unknown.
|
||||||
|
|
||||||
|
| Area | Question | State | Answer | Evidence |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| Scope Outcomes | Which journey is assessed? | `answered` | An internal operator authors, validates, builds, queues, sends and reconciles a Campaign with managed attachments. | — |
|
||||||
|
| Scope Outcomes | Is Workflow in scope? | `answered` | No; Workflow is planned and explicitly postponed. | — |
|
||||||
|
| Scope Outcomes | Which users, roles, tenants, organization units, and delegated functions participate? | `assumed` | One internal tenant or office with controlled Campaign operators; detailed organization and delegation shape remains target-specific. | — |
|
||||||
|
| Scope Outcomes | What constitutes pilot success and production acceptance? | `answered` | Pilot success requires the bounded Campaign journey and proof checks; production additionally requires installed-artifact, provider, privacy, security, operations, recovery, and approval evidence. | — |
|
||||||
|
| Data Policy | Which data classes and legal bases apply? | `not_assessed` | — | — |
|
||||||
|
| Data Policy | What retention, deletion, archive and legal-hold rules apply? | `not_assessed` | — | — |
|
||||||
|
| Data Policy | Which privacy, security, residency, minimization, access, and external-disclosure constraints apply? | `not_assessed` | — | — |
|
||||||
|
| Identity Integrations | May the pilot use local GovOPlaN accounts? | `assumed` | Yes; federation is outside the verified composition. | — |
|
||||||
|
| Identity Integrations | Which target SMTP/IMAP service and policy apply? | `not_assessed` | — | — |
|
||||||
|
| Identity Integrations | Which identity protocols, MFA, joiner/mover/leaver, service-account, and break-glass rules are mandatory? | `not_assessed` | — | — |
|
||||||
|
| Identity Integrations | Which connector protocols, versions, directions, authentication, certificate, rate-limit, egress, and degraded-mode requirements apply? | `not_assessed` | — | — |
|
||||||
|
| Workload Growth | What are Campaign frequency, recipients per Campaign, send window, import size and attachment volume? | `not_assessed` | — | — |
|
||||||
|
| Workload Growth | What are tenant, named-user, active-user, concurrent-user, and peak-request assumptions? | `not_assessed` | — | — |
|
||||||
|
| Workload Growth | What are tenant, user, concurrency, file, database, queue and audit growth assumptions? | `not_assessed` | — | — |
|
||||||
|
| Workload Growth | What connector traffic, scheduled-job, batch, queue-depth, queue-age, and external-rate-limit peaks apply? | `not_assessed` | — | — |
|
||||||
|
| Availability Operations | What availability, RPO and RTO are required? | `not_assessed` | — | — |
|
||||||
|
| Availability Operations | Who operates database, queue, storage, TLS, secrets, monitoring, backup and incident response? | `not_assessed` | — | — |
|
||||||
|
| Availability Operations | Which hosting, network-zone, egress, proxy, DNS, NTP, certificate-authority, residency, or disconnected-operation constraints apply? | `not_assessed` | — | — |
|
||||||
|
| Procurement Decisions | Which licensing, accessibility, security, certification, support and procurement conditions are mandatory? | `not_assessed` | — | — |
|
||||||
|
|
||||||
|
## Functional capability matrix
|
||||||
|
|
||||||
|
| Requirement | Status | Evidence | Conditions and gaps | Recommendation and proof |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **platform.composition**<br>Compose enabled backend and WebUI modules without hard optional-module dependencies. | `verified` | test/committed_source: govoplan-core/tests/test_module_system.py; contract/current_workspace: govoplan/tools/checks/check-contracts.py (43 modules, 33 providers, 19 requirements, no issues) | Condition: Package integration is verified; repeat checks on the installed target composition.; Gap: No target deployment acceptance is recorded.; Risk: A reproducible module graph can still be installed or configured incorrectly. | Use the signed stable catalog and verify the minimal Campaign composition after installation.<br>**Proof:** Run contract, migration, API and WebUI module-permutation gates on the installed release. |
|
||||||
|
| **access.local**<br>Provide tenant-scoped local accounts, sessions, API keys and RBAC. | `verified` | test/committed_source: govoplan-access/tests/test_auth_dependencies.py; test/committed_source: govoplan-core/tests/test_api_smoke.py#cookie-session-csrf | Condition: Pilot accepts local accounts.; Gap: MFA and federated lifecycle are not part of this conclusion.; Risk: Manual account lifecycle may not satisfy production identity policy. | Use controlled local pilot accounts and define break-glass/bootstrap rules.<br>**Proof:** Exercise joiner, role change, suspension and protected-owner recovery. |
|
||||||
|
| **campaign.journey**<br>Author, validate, build, queue, send, reconcile and report a Campaign with frozen execution evidence. | `verified` | test/committed_source: govoplan-core/tests/test_api_smoke.py#campaign-create-validate-build-mock-send; test/committed_source: govoplan-campaign/tests (Campaign v0.1.10 is exactly the catalog-selected tagged source); configuration/committed_source: https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json#sequence-202607220843 (Core v0.1.13 and Campaign v0.1.10 have matching catalogued Python and WebUI refs) | Condition: This verifies implementation paths, not target-provider delivery.; Gap: Usability and target-provider acceptance remain separate.; Risk: Package integration does not prove provider behavior or production operations. | Use the catalogued Campaign release for usability and target-provider acceptance.<br>**Proof:** Run the complete journey with safe data and the target-like mail service. |
|
||||||
|
| **files.managed_attachments**<br>Store and resolve managed Campaign attachments on durable storage. | `verified` | test/current_workspace: govoplan-files/tests (14 tests passed); test/current_workspace: govoplan-campaign/tests/test_attachment_building.py | Condition: Deployment provides a durable storage root.; Gap: Target backup and restore are not verified.; Risk: Node-local storage prevents safe independent API scaling. | Use durable local storage for the pilot and assess object/shared storage before scaling.<br>**Proof:** Back up and restore files together with database references. |
|
||||||
|
| **mail.smtp_imap**<br>Send Campaign mail through SMTP and optionally append sent messages through IMAP. | `available_unconfigured` | test/current_workspace: govoplan-mail/tests (22 tests passed); documentation/documented_model: govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md | Condition: Use a dedicated non-production service account and safe recipients.; Gap: No target provider, TLS chain, throttling or bounce/reply process was exercised.; Risk: Ambiguous provider outcomes can cause duplicate-send risk if reconciled incorrectly. | Run target-like interoperability and failure drills before production use.<br>**Proof:** Prove SMTP acceptance, IMAP append, throttling and outcome reconciliation. |
|
||||||
|
| **addresses.recipient_sources**<br>Select reusable address lists as Campaign recipient sources. | `available_unconfigured` | test/current_workspace: govoplan-addresses/tests (14 tests passed) | Condition: Enable the Addresses module explicitly.; Gap: Addresses is disabled in the pinned root profile.; Risk: Recipient governance may differ between source data and frozen Campaign evidence. | Enable only when reusable lists are a pilot requirement.<br>**Proof:** Build a Campaign from a source list and verify immutable recipient provenance. |
|
||||||
|
| **audit.local**<br>Retain tenant/system audit evidence and retry governed audit events. | `verified` | test/current_workspace: govoplan-audit/tests (5 tests passed) | Condition: Conclusion covers local database evidence only.; Gap: No central sink, retention enforcement or tamper-evident archive is verified.; Risk: Local audit evidence may not satisfy organizational records or SIEM requirements. | Define retention and export requirements before production approval.<br>**Proof:** Exercise privileged-event review, retention and any required external export. |
|
||||||
|
| **identity.federation**<br>Integrate external LDAP/AD, OIDC/SAML or SCIM identity infrastructure. | `scaffold` | documentation/documented_model: govoplan-idm/README.md | Gap: No end-to-end provider connector or federated login is verified.; Risk: Federation-dependent organizations cannot use the current pilot composition without extra implementation. | Use local pilot accounts or assess and implement the selected provider path.<br>**Proof:** Run provider metadata, login/provisioning, deprovisioning and failure tests. |
|
||||||
|
| **compliance.export_control**<br>Screen persons and organizations against embargo/sanctions lists with review evidence. | `planned` | issue/documented_model: https://git.add-ideas.de/GovOPlaN/govoplan/issues/12 | Gap: No provider, list provenance, match policy, review flow or legal evidence exists.; Risk: The current composition must not be represented as performing export-control screening. | Keep outside pilot claims until the user story is implemented and legally validated.<br>**Proof:** Validate list ingestion, versioning, matching, false-positive review and audit evidence. |
|
||||||
|
| **workflow**<br>Orchestrate the journey through Workflow. | `planned` | observation/documented_model: Assessment scope (Explicitly postponed) | Gap: Workflow is outside this assessment.; Risk: Including it would overstate the assessed composition. | Do not enable or claim Workflow for this reference pilot.<br>**Proof:** Reassess in a later Workflow-focused composition. |
|
||||||
|
|
||||||
|
## Infrastructure matrix
|
||||||
|
|
||||||
|
| Requirement | Status | Evidence | Conditions and gaps | Recommendation and proof |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **runtime.web_api**<br>Serve matching WebUI and API artifacts with health endpoints. | `verified` | test/committed_source: govoplan-core/tests/test_module_system.py; route/committed_source: govoplan-core/src/govoplan_core/server/fastapi.py#/health | Condition: Materialize the matching catalogued artifacts in the target.; Gap: No production image or service bundle is supplied by the profile.; Risk: Editable source processes are unsuitable as a production artifact. | Install matching catalogued WebUI/API refs and supervise them as immutable artifacts.<br>**Proof:** Deploy the built artifacts and run health/module-route checks. |
|
||||||
|
| **runtime.worker**<br>Run durable asynchronous Campaign jobs. | `available_unconfigured` | configuration/current_workspace: govoplan/tools/launch/launch-production-like-dev.sh | Condition: Redis and a supervised worker are required when Celery is enabled.; Gap: Target heartbeat, restart and queue-age alerting are not proved.; Risk: Queued work can stall silently without monitoring. | Start one worker for the pilot and split queues only after measurement.<br>**Proof:** Interrupt and restart a worker while preserving job/reconciliation safety. |
|
||||||
|
| **runtime.scheduler**<br>Run periodic recovery and cleanup safely. | `partial` | test/committed_source: govoplan-calendar/tests/test_outbox.py (Committed and pushed after the catalogued Calendar v0.1.8 tag) | Condition: Calendar outbox and recovery work is remote-integrated source but not stable-package-integrated.; Gap: No distributed leader election or target supervision is established.; Risk: Multiple schedulers can duplicate periodic dispatch without locking. | Omit from the Campaign-only pilot or run one supervised instance.<br>**Proof:** Prove missed-schedule recovery and single-leader behavior. |
|
||||||
|
| **data.postgresql**<br>Persist application state in PostgreSQL with explicit migrations. | `verified` | configuration/committed_source: govoplan/dev/postgres; test/committed_source: govoplan/tools/checks/postgres-integration-check.py | Condition: Target database remains deployment-owned.; Gap: HA, patching, WAL policy and capacity are not assessed.; Risk: A single unprotected database is a system-wide failure point. | Use managed or dedicated PostgreSQL with explicit migration and backup controls.<br>**Proof:** Run migrations and restore a target-like database. |
|
||||||
|
| **queue.redis**<br>Provide the Celery broker and queue persistence. | `available_unconfigured` | configuration/current_workspace: govoplan/dev/production-like/docker-compose.yml#redis | Gap: Authentication, TLS, eviction, HA and queue-loss policy are not assessed.; Risk: Broker loss or eviction can delay work even when database business state survives. | Configure private persistent Redis and monitor queue age/depth.<br>**Proof:** Exercise broker interruption and worker recovery. |
|
||||||
|
| **storage.local**<br>Persist managed files on a durable single-node/shared path. | `verified` | contract/committed_source: govoplan-files/src/govoplan_files/backend/storage/backends.py | Condition: Path is durable, private, writable and backed up.; Gap: Node-local storage cannot support independent API replicas.; Risk: Files can be lost or become inconsistent with database state. | Use for a bounded pilot only with coordinated backup.<br>**Proof:** Restore files and verify all database references. |
|
||||||
|
| **storage.object**<br>Use S3-compatible storage for independently scalable file persistence. | `partial` | test/current_workspace: govoplan-files/tests/test_connector_providers.py | Gap: No chosen target service or storage-backend interoperability drill.; Risk: Provider semantics, CA or lifecycle mismatch can break file access/retention. | Select and exercise the target object store before horizontal scaling.<br>**Proof:** Upload, retrieve, version, back up and restore representative objects. |
|
||||||
|
| **edge.proxy_tls**<br>Terminate HTTPS and enforce proxy/security policy. | `external_system` | route/committed_source: govoplan-ops/src/govoplan_ops/backend/api/v1/routes.py#deployment-security | Gap: No proxy, certificates, renewal, header or request-limit configuration is shipped here.; Risk: Incorrect proxy/cookie/CORS configuration can expose sessions or block legitimate use. | Supply and monitor the edge through the target platform.<br>**Proof:** Run external TLS/header/cookie/CORS and upload-limit tests. |
|
||||||
|
| **security.secret_store**<br>Inject and rotate master, database, mail and connector secrets. | `external_system` | configuration/committed_source: govoplan/.env.example | Gap: No target secret manager or rotation drill is selected.; Risk: Loss of the master key makes encrypted credentials unavailable; leakage compromises connectors. | Use target-native secret injection and document rotation/recovery.<br>**Proof:** Rotate a non-production credential and recover from a protected backup. |
|
||||||
|
| **identity.access**<br>Authenticate users and enforce tenant-scoped authorization through the selected identity mode. | `verified` | test/committed_source: govoplan-access/tests/test_auth_dependencies.py; test/committed_source: govoplan-core/tests/test_api_smoke.py#cookie-session-csrf | Condition: The bounded pilot accepts local GovOPlaN accounts.; Gap: Target MFA, federation, provisioning, and joiner/mover/leaver requirements are not assessed.; Risk: A local-only identity topology may not satisfy institutional production policy. | Use controlled local pilot accounts and assess the mandatory production identity topology separately.<br>**Proof:** Exercise login, role change, account suspension, protected bootstrap, and break-glass recovery in the target. |
|
||||||
|
| **connectors.mail**<br>Reach the selected SMTP/IMAP and other external connector endpoints under explicit network and provider policy. | `available_unconfigured` | test/current_workspace: govoplan-mail/tests (Protocol adapters have direct tests; no target provider was exercised); documentation/documented_model: govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md | Condition: The deployment supplies DNS, egress, proxy, CA trust, scoped service accounts, and provider limits.; Gap: No target endpoint, TLS chain, throttling, sender policy, bounce/reply path, or disclosure agreement is assessed.; Risk: Provider rejection, delay, or ambiguous outcomes can affect delivery and evidence completeness. | Use a dedicated safe provider account for the pilot and require target interoperability evidence before production.<br>**Proof:** Exercise target-like SMTP acceptance, IMAP append, throttling, outage, retry, and reconciliation through the approved network path. |
|
||||||
|
| **operations.monitoring**<br>Detect API, database, worker, queue, storage and delivery degradation. | `partial` | route/committed_source: govoplan-ops/src/govoplan_ops/backend/api/v1/routes.py#/ops/readiness; contract/committed_source: govoplan-core/src/govoplan_core/server/fastapi.py#slow-request-logging | Gap: No metrics exporter, log collector, dashboards, alert routes or SLO is verified.; Risk: Failures and queue backlog can remain unnoticed. | Integrate external monitoring before small production.<br>**Proof:** Trigger each readiness/delivery failure and verify an actionable alert. |
|
||||||
|
| **operations.audit**<br>Retain, monitor, review, and where required export security and business audit evidence. | `partial` | test/current_workspace: govoplan-audit/tests (Local audit persistence and retry behavior are exercised) | Condition: Local database audit evidence is part of coordinated backup and access review.; Gap: Target retention enforcement, tamper-evident export, SIEM integration, alerting, and privileged review are not verified.; Risk: Local evidence alone may not meet institutional security, records, or incident-response requirements. | Define the target audit retention, export, monitoring, and review controls before production approval.<br>**Proof:** Exercise privileged-event review, retention, export failure/retry, and target SIEM or archive ingestion. |
|
||||||
|
| **operations.backup_restore**<br>Back up and restore database, files, configuration and keys as a coherent service. | `partial` | documentation/documented_model: govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md; issue/documented_model: https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/29 | Gap: No target full-service restore drill or measured RPO/RTO exists.; Risk: Partial restore can produce missing files, unusable secrets or inconsistent evidence. | Treat Core #29 and a target restore drill as a production gate.<br>**Proof:** Restore the whole service into an isolated environment and measure it. |
|
||||||
|
| **operations.disaster_recovery**<br>Recover the service after site or dependency loss within agreed RPO/RTO. | `not_assessed` | absence/current_workspace: No target DR plan or exercise evidence supplied | Gap: RPO/RTO, off-site copies, recovery order, failover, communications and exercise schedule are unknown.; Risk: Service and evidence may be unrecoverable after a major incident. | Define and exercise DR before any availability commitment.<br>**Proof:** Run a documented end-to-end recovery exercise. |
|
||||||
|
## Data flows and trust boundaries
|
||||||
|
|
||||||
|
| Flow | From → to | Data | Trust boundary | Controls |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `browser.api` | User browser → Reverse proxy and GovOPlaN WebUI/API | Session and CSRF cookies; Campaign content; Recipient personal data; Managed files | Client/public to application | HTTPS; Exact CORS origins; Secure cookies; Tenant and RBAC enforcement; Request limits |
|
||||||
|
| `api.database` | GovOPlaN API and workers → PostgreSQL | Tenant and identity records; Campaign drafts, snapshots and jobs; Connector metadata; Audit evidence | Application to primary state store | Dedicated database identity; Private or encrypted transport; Migrations; Backup and retention |
|
||||||
|
| `api.queue.worker` | GovOPlaN API → Redis and Celery worker | Job identifiers; Queue routing and retry metadata | Request plane to asynchronous processing plane | Private authenticated broker; Bounded payloads; Idempotent claims; Queue monitoring |
|
||||||
|
| `worker.mail` | GovOPlaN Campaign worker → External SMTP and IMAP services | Recipient addresses; Message bodies; Attachments; Sent-message copy | GovOPlaN to external communication provider | Scoped service account; TLS and CA policy; Sender and recipient policy; Rate limits; Outcome reconciliation |
|
||||||
|
| `worker.connectors` | GovOPlaN connector worker → External address, file, object or calendar service | Addresses; Files and provenance; Calendar resources | GovOPlaN to organizational/external content systems | Explicit sync direction; Scoped credentials; Endpoint allow-list; Provenance; Conflict and reconciliation policy |
|
||||||
|
|
||||||
|
## Risks and residual risks
|
||||||
|
|
||||||
|
| Risk | Impact | Treatment | Owner | Residual risk |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| **risk.reproducibility**<br>The signed package selection is reproducible but has not been accepted as an installed target composition. | Installation or configuration drift can still produce uncertain deployed behavior. | Materialize the signed catalog in an isolated target and run installed-artifact acceptance gates. | unassigned | Module and environment differences still require release-environment verification. |
|
||||||
|
| **risk.delivery_provider**<br>Target SMTP/IMAP behavior and failure modes are unproved. | Failed, delayed or duplicate communication and incomplete evidence. | Run target-like interoperability, throttling and uncertainty drills. | unassigned | External provider outages and ambiguous outcomes remain operational risks. |
|
||||||
|
| **risk.recovery**<br>Backup/restore and disaster recovery are not demonstrated across all state and keys. | Irrecoverable or inconsistent service after loss. | Complete Core #29 and an isolated full-service restore/DR exercise. | unassigned | Recovery time and data loss remain bounded by the selected external infrastructure. |
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
- Proceed only with a controlled internal Campaign pilot after the bounded proof checks pass.
|
||||||
|
- Use the minimal composition and enable Addresses only for an explicit reusable-recipient journey.
|
||||||
|
- Do not claim Workflow, export-control screening, identity federation or production DR as implemented.
|
||||||
|
- Treat installed-release acceptance, target mail proof, monitoring and a coherent restore drill as production gates.
|
||||||
|
|
||||||
|
## Proof-of-concept and promotion checks
|
||||||
|
|
||||||
|
1. Materialize the signed catalog into an isolated installation and rerun contract, migration and module-permutation gates against the installed artifacts.
|
||||||
|
2. Collect the isolated installation with the bounded installed-composition evidence contract; require exact enabled package/module versions, complete RECORD verification and immutable provenance anchored to this assessment.
|
||||||
|
3. Run a safe target-like Campaign through SMTP acceptance, IMAP append, reporting and audit.
|
||||||
|
4. Drill worker, Redis and ambiguous-delivery failures without duplicate sends.
|
||||||
|
5. Restore PostgreSQL, managed files, configuration and encrypted credentials and measure RPO/RTO.
|
||||||
|
6. Validate proxy/TLS, cookies/CORS, account bootstrap, secret redaction, monitoring and alert delivery.
|
||||||
|
7. Measure representative Campaign/file/queue/database load and external throttling.
|
||||||
|
8. Require separately issued, expiring and independently scope-authorized evidence before marking target environment, external provider or production approval proof as checked.
|
||||||
|
|
||||||
|
## Generation contract
|
||||||
|
|
||||||
|
This report is deterministic output from the schema-validated JSON companion.
|
||||||
|
The generator rejects duplicate JSON keys, schema drift, secret-bearing field
|
||||||
|
names, stale checked-in output, and oversized inputs. A new assessment or
|
||||||
|
release changes the canonical input hash and requires review of the affected
|
||||||
|
evidence and conclusions through the release-aware reassessment tool.
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
# GovOPlaN Capability and IT-Infrastructure Fit Assessment
|
# Supporting Narrative: 2026-07-22 Capability and Infrastructure Assessment
|
||||||
|
|
||||||
|
> **Canonical report:** The schema-validated human report is generated from the
|
||||||
|
> machine-readable input at
|
||||||
|
> [`CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md`](CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md).
|
||||||
|
> This file retains the original hand-authored evidence narrative and operator
|
||||||
|
> guidance; it is not maintained as a second set of conclusions.
|
||||||
|
|
||||||
> **Pinned historical evidence:** This document assesses the exact 2026-07-22
|
> **Pinned historical evidence:** This document assesses the exact 2026-07-22
|
||||||
> Campaign composition below. It is intentionally not updated to describe later
|
> Campaign composition below. It is intentionally not updated to describe later
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# DSAR Provider Coverage
|
||||||
|
|
||||||
|
This generated matrix is enforced by `tools/checks/check-dsar-coverage.py`.
|
||||||
|
A migration-owning module must register and document its canonical DSAR provider.
|
||||||
|
Every other active module requires a reviewed explanation of why it owns no
|
||||||
|
persistent subject-data store. Adding a migration invalidates that explanation.
|
||||||
|
|
||||||
|
- Active modules: 68
|
||||||
|
- Registered and documented DSAR providers: 48
|
||||||
|
- Reviewed no-store rationales: 20
|
||||||
|
- Unexplained coverage gaps: 0
|
||||||
|
|
||||||
|
| Module | Repository | Persistence | Coverage | Rationale |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `access` | `govoplan-access` | Migration-owned | Provider | Provider `privacy.dsar.access` is registered and documented. |
|
||||||
|
| `addresses` | `govoplan-addresses` | Migration-owned | Provider | Provider `privacy.dsar.addresses` is registered and documented. |
|
||||||
|
| `admin` | `govoplan-admin` | Migration-owned | Provider | Provider `privacy.dsar.admin` is registered and documented. |
|
||||||
|
| `approvals` | `govoplan-approvals` | Migration-owned | Provider | Provider `privacy.dsar.approvals` is registered and documented. |
|
||||||
|
| `assets` | `govoplan-assets` | No module migration | Reviewed no-store rationale | Contract-only module: asset persistence and lifecycle APIs are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `audit` | `govoplan-audit` | Migration-owned | Provider | Provider `privacy.dsar.audit` is registered and documented. |
|
||||||
|
| `booking` | `govoplan-booking` | No module migration | Reviewed no-store rationale | Contract-only module: booking persistence and reservation workflows are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `calendar` | `govoplan-calendar` | Migration-owned | Provider | Provider `privacy.dsar.calendar` is registered and documented. |
|
||||||
|
| `campaigns` | `govoplan-campaign` | Migration-owned | Provider | Provider `privacy.dsar.campaigns` is registered and documented. |
|
||||||
|
| `cases` | `govoplan-cases` | Migration-owned | Provider | Provider `privacy.dsar.cases` is registered and documented. |
|
||||||
|
| `certificates` | `govoplan-certificates` | No module migration | Reviewed no-store rationale | Contract-only module: certificate issuance and revocation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `committee` | `govoplan-committee` | Migration-owned | Provider | Provider `privacy.dsar.committee` is registered and documented. |
|
||||||
|
| `connectors` | `govoplan-connectors` | Migration-owned | Provider | Provider `privacy.dsar.connectors` is registered and documented. |
|
||||||
|
| `consultation` | `govoplan-consultation` | No module migration | Reviewed no-store rationale | Contract-only module: consultation submissions and evaluation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `contracts` | `govoplan-contracts` | No module migration | Reviewed no-store rationale | Contract-only module: contract, amendment, and obligation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `dashboard` | `govoplan-dashboard` | Migration-owned | Provider | Provider `privacy.dsar.dashboard` is registered and documented. |
|
||||||
|
| `dataflow` | `govoplan-dataflow` | Migration-owned | Provider | Provider `privacy.dsar.dataflow` is registered and documented. |
|
||||||
|
| `datasources` | `govoplan-datasources` | Migration-owned | Provider | Provider `privacy.dsar.datasources` is registered and documented. |
|
||||||
|
| `decisions` | `govoplan-decisions` | Migration-owned | Provider | Provider `privacy.dsar.decisions` is registered and documented. |
|
||||||
|
| `dist_lists` | `govoplan-dist-lists` | Migration-owned | Provider | Provider `privacy.dsar.dist_lists` is registered and documented. |
|
||||||
|
| `docs` | `govoplan-docs` | Migration-owned | Provider | Provider `privacy.dsar.docs` is registered and documented. |
|
||||||
|
| `encryption` | `govoplan-encryption` | Migration-owned | Provider | Provider `privacy.dsar.encryption` is registered and documented. |
|
||||||
|
| `evaluation` | `govoplan-evaluation` | No module migration | Reviewed no-store rationale | Contract-only module: evaluation runs, responses, and scores are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `facilities` | `govoplan-facilities` | No module migration | Reviewed no-store rationale | Contract-only module: facility and maintenance persistence are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `files` | `govoplan-files` | Migration-owned | Provider | Provider `privacy.dsar.files` is registered and documented. |
|
||||||
|
| `forms` | `govoplan-forms` | Migration-owned | Provider | Provider `privacy.dsar.forms` is registered and documented. |
|
||||||
|
| `forms_runtime` | `govoplan-forms-runtime` | Migration-owned | Provider | Provider `privacy.dsar.forms_runtime` is registered and documented. |
|
||||||
|
| `grants` | `govoplan-grants` | No module migration | Reviewed no-store rationale | Contract-only module: grant applications, awards, and monitoring are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `helpdesk` | `govoplan-helpdesk` | Migration-owned | Provider | Provider `privacy.dsar.helpdesk` is registered and documented. |
|
||||||
|
| `identity` | `govoplan-identity` | Migration-owned | Provider | Provider `privacy.dsar.identity` is registered and documented. |
|
||||||
|
| `identity_trust` | `govoplan-identity-trust` | Migration-owned | Provider | Provider `privacy.dsar.identity_trust` is registered and documented. |
|
||||||
|
| `idm` | `govoplan-idm` | Migration-owned | Provider | Provider `privacy.dsar.idm` is registered and documented. |
|
||||||
|
| `inspections` | `govoplan-inspections` | No module migration | Reviewed no-store rationale | Contract-only module: inspections, findings, and measures are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `learning` | `govoplan-learning` | No module migration | Reviewed no-store rationale | Contract-only module: learning offers, enrollment, and completion are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `mail` | `govoplan-mail` | Migration-owned | Provider | Provider `privacy.dsar.mail` is registered and documented. |
|
||||||
|
| `mandates` | `govoplan-mandates` | Migration-owned | Provider | Provider `privacy.dsar.mandates` is registered and documented. |
|
||||||
|
| `notifications` | `govoplan-notifications` | Migration-owned | Provider | Provider `privacy.dsar.notifications` is registered and documented. |
|
||||||
|
| `ops` | `govoplan-ops` | No module migration | Reviewed no-store rationale | Projection-only module: Ops reads bounded platform and provider status; durable recovery evidence remains owned by Core and domain modules. |
|
||||||
|
| `organizations` | `govoplan-organizations` | Migration-owned | Provider | Provider `privacy.dsar.organizations` is registered and documented. |
|
||||||
|
| `parties` | `govoplan-parties` | Migration-owned | Provider | Provider `privacy.dsar.parties` is registered and documented. |
|
||||||
|
| `payments` | `govoplan-payments` | Migration-owned | Provider | Provider `privacy.dsar.payments` is registered and documented. |
|
||||||
|
| `permits` | `govoplan-permits` | No module migration | Reviewed no-store rationale | Contract-only module: permit applications, assessments, and decisions are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `policy` | `govoplan-policy` | Migration-owned | Provider | Provider `privacy.dsar.policy` is registered and documented. |
|
||||||
|
| `poll` | `govoplan-poll` | Migration-owned | Provider | Provider `privacy.dsar.poll` is registered and documented. |
|
||||||
|
| `portal` | `govoplan-portal` | No module migration | Reviewed no-store rationale | Projection-only module: Portal stores no applicant records; Services, Forms Runtime, Cases, and Postbox own and export authoritative subject data. |
|
||||||
|
| `postbox` | `govoplan-postbox` | Migration-owned | Provider | Provider `privacy.dsar.postbox` is registered and documented. |
|
||||||
|
| `procurement` | `govoplan-procurement` | No module migration | Reviewed no-store rationale | Contract-only module: procurement procedures, tenders, and awards are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `projects` | `govoplan-projects` | Migration-owned | Provider | Provider `privacy.dsar.projects` is registered and documented. |
|
||||||
|
| `quick_access` | `govoplan-quick-access` | Migration-owned | Provider | Provider `privacy.dsar.quick_access` is registered and documented. |
|
||||||
|
| `records` | `govoplan-records` | Migration-owned | Provider | Provider `privacy.dsar.records` is registered and documented. |
|
||||||
|
| `reporting` | `govoplan-reporting` | Migration-owned | Provider | Provider `privacy.dsar.reporting` is registered and documented. |
|
||||||
|
| `resources` | `govoplan-resources` | No module migration | Reviewed no-store rationale | Contract-only module: resource catalog and allocation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||||
|
| `rest` | `govoplan-rest` | No module migration | Reviewed no-store rationale | Transport-only module: REST binds explicitly published functions and owns no domain or subject-data store. |
|
||||||
|
| `risk_compliance` | `govoplan-risk-compliance` | Migration-owned | Provider | Provider `privacy.dsar.risk_compliance` is registered and documented. |
|
||||||
|
| `scheduling` | `govoplan-scheduling` | Migration-owned | Provider | Provider `privacy.dsar.scheduling` is registered and documented. |
|
||||||
|
| `search` | `govoplan-search` | Migration-owned | Provider | Provider `privacy.dsar.search` is registered and documented. |
|
||||||
|
| `services` | `govoplan-services` | Migration-owned | Provider | Provider `privacy.dsar.services` is registered and documented. |
|
||||||
|
| `soap` | `govoplan-soap` | No module migration | Reviewed no-store rationale | Transport-only module: SOAP binds explicitly published operations and owns no domain or subject-data store. |
|
||||||
|
| `tasks` | `govoplan-tasks` | Migration-owned | Provider | Provider `privacy.dsar.tasks` is registered and documented. |
|
||||||
|
| `templates` | `govoplan-templates` | Migration-owned | Provider | Provider `privacy.dsar.templates` is registered and documented. |
|
||||||
|
| `tenancy` | `govoplan-tenancy` | No module migration | Reviewed no-store rationale | Orchestration module: tenant lifecycle and settings use Core-owned storage; Access covers account and membership subject data. |
|
||||||
|
| `tickets` | `govoplan-tickets` | Migration-owned | Provider | Provider `privacy.dsar.tickets` is registered and documented. |
|
||||||
|
| `transparency` | `govoplan-transparency` | No module migration | Reviewed no-store rationale | Contract-only module: requests, disclosure reviews, and publications are not persisted; reassess before adding a migration-owned store. |
|
||||||
|
| `views` | `govoplan-views` | Migration-owned | Provider | Provider `privacy.dsar.views` is registered and documented. |
|
||||||
|
| `voting` | `govoplan-voting` | Migration-owned | Provider | Provider `privacy.dsar.voting` is registered and documented. |
|
||||||
|
| `wiki` | `govoplan-wiki` | Migration-owned | Provider | Provider `privacy.dsar.wiki` is registered and documented. |
|
||||||
|
| `workflow` | `govoplan-workflow` | No module migration | Reviewed no-store rationale | Presentation-only module: Workflow edits and projects Workflow Engine state; Workflow Engine owns persistence and DSAR coverage. |
|
||||||
|
| `workflow_engine` | `govoplan-workflow-engine` | Migration-owned | Provider | Provider `privacy.dsar.workflow_engine` is registered and documented. |
|
||||||
|
|
||||||
|
Provider search, export minimization, retention, and erasure behavior remains
|
||||||
|
documented and tested by each owning module. This matrix verifies adoption and
|
||||||
|
ownership coverage; Core continues to test disabled providers, partial failure,
|
||||||
|
retry, authorization evidence, and horizontally coordinated execution.
|
||||||
@@ -109,7 +109,9 @@ semantics as authenticated navigation routes.
|
|||||||
| `/scheduling/public/:requestId/:token` | Scheduling | Public signed token | Public participation | Scheduling #8 complete in `c17cbda` |
|
| `/scheduling/public/:requestId/:token` | Scheduling | Public signed token | Public participation | Scheduling #8 complete in `c17cbda` |
|
||||||
| `/search` | Search | `search:result:read` | Keyboard-first global/context overlay and full results fallback | Search pattern migration complete in [Search #4](https://git.add-ideas.de/GovOPlaN/govoplan-search/issues/4); durable evidence in `govoplan-search/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
| `/search` | Search | `search:result:read` | Keyboard-first global/context overlay and full results fallback | Search pattern migration complete in [Search #4](https://git.add-ideas.de/GovOPlaN/govoplan-search/issues/4); durable evidence in `govoplan-search/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||||
| `/templates` | Templates | Template read/write/publish/render/admin | Governed library, immutable-revision editor, compatibility preview, and render evidence | Templates pattern migration complete in [Templates #5](https://git.add-ideas.de/GovOPlaN/govoplan-templates/issues/5), commit `72fafa2` |
|
| `/templates` | Templates | Template read/write/publish/render/admin | Governed library, immutable-revision editor, compatibility preview, and render evidence | Templates pattern migration complete in [Templates #5](https://git.add-ideas.de/GovOPlaN/govoplan-templates/issues/5), commit `72fafa2` |
|
||||||
|
| `/tickets` | Tickets | `tickets:ticket:read` | Governed operational queue/detail workspace with distinct report, triage, assignment, resolution, comment, reference and removal boundaries | Tickets vertical slice and pattern migration complete in [Tickets #1](https://git.add-ideas.de/GovOPlaN/govoplan-tickets/issues/1), release `v0.1.20` |
|
||||||
| `/voting` | Voting | `voting:ballot:read` | Governed ballot workspace | Voting pattern migration complete in [Voting #1](https://git.add-ideas.de/GovOPlaN/govoplan-voting/issues/1), commit `2625990` |
|
| `/voting` | Voting | `voting:ballot:read` | Governed ballot workspace | Voting pattern migration complete in [Voting #1](https://git.add-ideas.de/GovOPlaN/govoplan-voting/issues/1), commit `2625990` |
|
||||||
|
| `/wiki` | Wiki | `wiki:page:read` | Governed space-tree/page workspace with draft editing, immutable revision comparison, publication, comments, typed references and archival | Native Wiki vertical slice and pattern migration implemented in [Wiki #1](https://git.add-ideas.de/GovOPlaN/govoplan-wiki/issues/1), release `v0.1.20` |
|
||||||
| `/workflow` | Workflow | Definition read or instance admin | Native BPMN editor, governed revision actions and execution evidence | Workflow pattern migration complete in [Workflow #15](https://git.add-ideas.de/GovOPlaN/govoplan-workflow/issues/15); durable evidence in `govoplan-workflow/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
| `/workflow` | Workflow | Definition read or instance admin | Native BPMN editor, governed revision actions and execution evidence | Workflow pattern migration complete in [Workflow #15](https://git.add-ideas.de/GovOPlaN/govoplan-workflow/issues/15); durable evidence in `govoplan-workflow/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||||
|
|
||||||
## Final Module Closure Evidence
|
## Final Module Closure Evidence
|
||||||
@@ -323,8 +325,8 @@ The generated manifest snapshot reports no WebUI package for:
|
|||||||
`govoplan-learning`, `govoplan-mandates`, `govoplan-parties`,
|
`govoplan-learning`, `govoplan-mandates`, `govoplan-parties`,
|
||||||
`govoplan-permits`, `govoplan-poll`, `govoplan-procurement`,
|
`govoplan-permits`, `govoplan-poll`, `govoplan-procurement`,
|
||||||
`govoplan-records`, `govoplan-resources`, `govoplan-rest`,
|
`govoplan-records`, `govoplan-resources`, `govoplan-rest`,
|
||||||
`govoplan-services`, `govoplan-soap`, `govoplan-tickets`,
|
`govoplan-services`, `govoplan-soap`, `govoplan-transparency`, and
|
||||||
`govoplan-transparency`, `govoplan-wiki`, and `govoplan-workflow-engine`.
|
`govoplan-workflow-engine`.
|
||||||
|
|
||||||
Tenancy does provide composed administration surfaces despite having no direct
|
Tenancy does provide composed administration surfaces despite having no direct
|
||||||
route. This section is only negative package evidence; connector-only,
|
route. This section is only negative package evidence; connector-only,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Read it together with:
|
|||||||
- the [institutional governance target architecture](../../architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md)
|
- the [institutional governance target architecture](../../architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md)
|
||||||
- the [selected reference-journey program](../REFERENCE_JOURNEY_PROGRAM.md)
|
- the [selected reference-journey program](../REFERENCE_JOURNEY_PROGRAM.md)
|
||||||
- the [current strategy status](../STRATEGY_STATUS.md)
|
- the [current strategy status](../STRATEGY_STATUS.md)
|
||||||
- the [pinned Campaign capability and infrastructure fit assessment](../../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
- the [generated, pinned Campaign capability and infrastructure fit assessment](../../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||||
- the [interface pattern language](../../architecture/INTERFACE_PATTERN_LANGUAGE.md)
|
- the [interface pattern language](../../architecture/INTERFACE_PATTERN_LANGUAGE.md)
|
||||||
- the [interface surface inventory](../../evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
- the [interface surface inventory](../../evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
||||||
- the [module contract and install model](../../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
- the [module contract and install model](../../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||||
|
|||||||
@@ -4,85 +4,85 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "govoplan"
|
name = "govoplan"
|
||||||
version = "0.1.18"
|
version = "0.1.34"
|
||||||
description = "Developer convenience package for a versioned GovOPlaN composition"
|
description = "Developer convenience package for a versioned GovOPlaN composition"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
license = { text = "AGPL-3.0-or-later" }
|
license = { text = "AGPL-3.0-or-later" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"govoplan-core[server]==0.1.18",
|
"govoplan-core[server]==0.1.34",
|
||||||
"govoplan-tenancy==0.1.18",
|
"govoplan-tenancy==0.1.18",
|
||||||
"govoplan-organizations==0.1.18",
|
"govoplan-organizations==0.1.18",
|
||||||
"govoplan-identity==0.1.18",
|
"govoplan-identity==0.1.18",
|
||||||
"govoplan-idm==0.1.18",
|
"govoplan-idm==0.1.20",
|
||||||
"govoplan-access==0.1.18",
|
"govoplan-access==0.1.19",
|
||||||
"govoplan-admin==0.1.18",
|
"govoplan-admin==0.1.18",
|
||||||
"govoplan-policy==0.1.18",
|
"govoplan-policy==0.1.20",
|
||||||
"govoplan-audit==0.1.18",
|
"govoplan-audit==0.1.19",
|
||||||
"govoplan-dashboard==0.1.18",
|
"govoplan-dashboard==0.1.18",
|
||||||
"govoplan-files==0.1.18",
|
"govoplan-files==0.1.20",
|
||||||
"govoplan-mail==0.1.18",
|
"govoplan-mail==0.1.22",
|
||||||
"govoplan-campaign==0.1.18",
|
"govoplan-campaign==0.1.24",
|
||||||
"govoplan-calendar==0.1.18",
|
"govoplan-calendar==0.1.18",
|
||||||
"govoplan-docs==0.1.18",
|
"govoplan-docs==0.1.20",
|
||||||
"govoplan-ops==0.1.18",
|
"govoplan-ops==0.1.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
full = [
|
full = [
|
||||||
"govoplan-addresses==0.1.18",
|
"govoplan-addresses==0.1.18",
|
||||||
"govoplan-approvals==0.1.18",
|
"govoplan-approvals==0.1.18",
|
||||||
"govoplan-assets==0.1.18",
|
"govoplan-assets==0.1.19",
|
||||||
"govoplan-booking==0.1.18",
|
"govoplan-booking==0.1.19",
|
||||||
"govoplan-cases==0.1.18",
|
"govoplan-cases==0.1.20",
|
||||||
"govoplan-certificates==0.1.18",
|
"govoplan-certificates==0.1.19",
|
||||||
"govoplan-committee==0.1.18",
|
"govoplan-committee==0.1.18",
|
||||||
"govoplan-connectors==0.1.18",
|
"govoplan-connectors==0.1.22",
|
||||||
"govoplan-consultation==0.1.18",
|
"govoplan-consultation==0.1.19",
|
||||||
"govoplan-contracts==0.1.18",
|
"govoplan-contracts==0.1.19",
|
||||||
"govoplan-dataflow==0.1.18",
|
"govoplan-dataflow==0.1.20",
|
||||||
"govoplan-datasources==0.1.18",
|
"govoplan-datasources==0.1.20",
|
||||||
"govoplan-decisions==0.1.18",
|
"govoplan-decisions==0.1.18",
|
||||||
"govoplan-dist-lists==0.1.18",
|
"govoplan-dist-lists==0.1.18",
|
||||||
"govoplan-encryption==0.1.18",
|
"govoplan-encryption==0.1.18",
|
||||||
"govoplan-evaluation==0.1.18",
|
"govoplan-evaluation==0.1.19",
|
||||||
"govoplan-facilities==0.1.18",
|
"govoplan-facilities==0.1.19",
|
||||||
"govoplan-forms==0.1.18",
|
"govoplan-forms==0.1.19",
|
||||||
"govoplan-forms-runtime==0.1.18",
|
"govoplan-forms-runtime==0.1.18",
|
||||||
"govoplan-grants==0.1.18",
|
"govoplan-grants==0.1.19",
|
||||||
"govoplan-helpdesk==0.1.18",
|
"govoplan-helpdesk==0.1.20",
|
||||||
"govoplan-identity-trust==0.1.18",
|
"govoplan-identity-trust==0.1.18",
|
||||||
"govoplan-inspections==0.1.18",
|
"govoplan-inspections==0.1.19",
|
||||||
"govoplan-learning==0.1.18",
|
"govoplan-learning==0.1.19",
|
||||||
"govoplan-mandates==0.1.18",
|
"govoplan-mandates==0.1.18",
|
||||||
"govoplan-notifications==0.1.18",
|
"govoplan-notifications==0.1.18",
|
||||||
"govoplan-parties==0.1.18",
|
"govoplan-parties==0.1.18",
|
||||||
"govoplan-payments==0.1.20",
|
"govoplan-payments==0.1.20",
|
||||||
"govoplan-permits==0.1.18",
|
"govoplan-permits==0.1.19",
|
||||||
"govoplan-poll==0.1.18",
|
"govoplan-poll==0.1.19",
|
||||||
"govoplan-portal==0.1.18",
|
"govoplan-portal==0.1.19",
|
||||||
"govoplan-postbox==0.1.18",
|
"govoplan-postbox==0.1.19",
|
||||||
"govoplan-procurement==0.1.18",
|
"govoplan-procurement==0.1.19",
|
||||||
"govoplan-projects==0.1.18",
|
"govoplan-projects==0.1.18",
|
||||||
"govoplan-quick-access==0.1.18",
|
"govoplan-quick-access==0.1.19",
|
||||||
"govoplan-records==0.1.19",
|
"govoplan-records==0.1.19",
|
||||||
"govoplan-reporting==0.1.18",
|
"govoplan-reporting==0.1.18",
|
||||||
"govoplan-resources==0.1.18",
|
"govoplan-resources==0.1.19",
|
||||||
"govoplan-rest==0.1.18",
|
"govoplan-rest==0.1.19",
|
||||||
"govoplan-risk-compliance==0.1.18",
|
"govoplan-risk-compliance==0.1.18",
|
||||||
"govoplan-scheduling==0.1.18",
|
"govoplan-scheduling==0.1.18",
|
||||||
"govoplan-search==0.1.18",
|
"govoplan-search==0.1.18",
|
||||||
"govoplan-services==0.1.18",
|
"govoplan-services==0.1.18",
|
||||||
"govoplan-soap==0.1.18",
|
"govoplan-soap==0.1.19",
|
||||||
"govoplan-tasks==0.1.19",
|
"govoplan-tasks==0.1.20",
|
||||||
"govoplan-templates==0.1.18",
|
"govoplan-templates==0.1.18",
|
||||||
"govoplan-tickets==0.1.18",
|
"govoplan-tickets==0.1.20",
|
||||||
"govoplan-transparency==0.1.18",
|
"govoplan-transparency==0.1.19",
|
||||||
"govoplan-views==0.1.18",
|
"govoplan-views==0.1.19",
|
||||||
"govoplan-voting==0.1.18",
|
"govoplan-voting==0.1.18",
|
||||||
"govoplan-wiki==0.1.18",
|
"govoplan-wiki==0.1.20",
|
||||||
"govoplan-workflow==0.1.18",
|
"govoplan-workflow==0.1.21",
|
||||||
"govoplan-workflow-engine==0.1.18",
|
"govoplan-workflow-engine==0.1.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -4,15 +4,15 @@
|
|||||||
govoplan-tenancy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tenancy.git@v0.1.18
|
govoplan-tenancy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tenancy.git@v0.1.18
|
||||||
govoplan-organizations @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git@v0.1.18
|
govoplan-organizations @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git@v0.1.18
|
||||||
govoplan-identity @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity.git@v0.1.18
|
govoplan-identity @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity.git@v0.1.18
|
||||||
govoplan-idm @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git@v0.1.18
|
govoplan-idm @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git@v0.1.20
|
||||||
govoplan-access @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git@v0.1.18
|
govoplan-access @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git@v0.1.19
|
||||||
govoplan-admin @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git@v0.1.18
|
govoplan-admin @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git@v0.1.18
|
||||||
govoplan-policy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git@v0.1.18
|
govoplan-policy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git@v0.1.20
|
||||||
govoplan-audit @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git@v0.1.18
|
govoplan-audit @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git@v0.1.19
|
||||||
govoplan-dashboard @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git@v0.1.18
|
govoplan-dashboard @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git@v0.1.18
|
||||||
govoplan-files @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git@v0.1.18
|
govoplan-files @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git@v0.1.20
|
||||||
govoplan-mail @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git@v0.1.18
|
govoplan-mail @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git@v0.1.22
|
||||||
govoplan-campaign @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git@v0.1.18
|
govoplan-campaign @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git@v0.1.24
|
||||||
govoplan-calendar @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git@v0.1.18
|
govoplan-calendar @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git@v0.1.18
|
||||||
govoplan-docs @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git@v0.1.18
|
govoplan-docs @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git@v0.1.20
|
||||||
govoplan-ops @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git@v0.1.18
|
govoplan-ops @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git@v0.1.19
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
META_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
ASSESSMENT_TOOLS_ROOT = META_ROOT / "tools" / "assessments"
|
||||||
|
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
|
||||||
|
for tools_root in (ASSESSMENT_TOOLS_ROOT, RELEASE_TOOLS_ROOT):
|
||||||
|
if str(tools_root) not in sys.path:
|
||||||
|
sys.path.insert(0, str(tools_root))
|
||||||
|
|
||||||
|
from govoplan_assessment.report_generator import ( # noqa: E402
|
||||||
|
AssessmentGenerationError,
|
||||||
|
load_bounded_json,
|
||||||
|
render_assessment_markdown,
|
||||||
|
validate_report_input,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CapabilityFitGenerationTests(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls) -> None:
|
||||||
|
cls.assessment = json.loads(
|
||||||
|
(META_ROOT / "docs" / "capability-fit-current.json").read_text("utf-8")
|
||||||
|
)
|
||||||
|
cls.schema = json.loads(
|
||||||
|
(META_ROOT / "docs" / "capability-fit.schema.json").read_text("utf-8")
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_current_assessment_renders_every_conclusion_from_one_input(self) -> None:
|
||||||
|
validate_report_input(assessment=self.assessment, schema=self.schema)
|
||||||
|
|
||||||
|
first = render_assessment_markdown(self.assessment)
|
||||||
|
second = render_assessment_markdown(deepcopy(self.assessment))
|
||||||
|
|
||||||
|
self.assertEqual(first, second)
|
||||||
|
self.assertIn("## Facts", first)
|
||||||
|
self.assertIn("## Decisions", first)
|
||||||
|
self.assertIn("## Unresolved decisions", first)
|
||||||
|
self.assertIn("## Risks and residual risks", first)
|
||||||
|
self.assertIn("## Proof-of-concept and promotion checks", first)
|
||||||
|
self.assertIn("### Controlled Campaign pilot", first)
|
||||||
|
self.assertIn("### Small-production candidate", first)
|
||||||
|
self.assertIn("## Functional matrix context", first)
|
||||||
|
self.assertIn("### Manual workarounds", first)
|
||||||
|
self.assertIn("### Blockers", first)
|
||||||
|
self.assertIn("Workflow and workflow-driven user stories", first)
|
||||||
|
for status in self.schema["$defs"]["status"]["enum"]:
|
||||||
|
self.assertIn(f"`{status}`", first)
|
||||||
|
for collection in ("capabilities", "infrastructure", "data_flows"):
|
||||||
|
for item in self.assessment[collection]:
|
||||||
|
self.assertIn(item["id"], first)
|
||||||
|
infrastructure_ids = {
|
||||||
|
item["id"] for item in self.assessment["infrastructure"]
|
||||||
|
}
|
||||||
|
self.assertTrue(
|
||||||
|
{
|
||||||
|
"runtime.web_api",
|
||||||
|
"runtime.worker",
|
||||||
|
"runtime.scheduler",
|
||||||
|
"data.postgresql",
|
||||||
|
"queue.redis",
|
||||||
|
"storage.local",
|
||||||
|
"storage.object",
|
||||||
|
"edge.proxy_tls",
|
||||||
|
"identity.access",
|
||||||
|
"security.secret_store",
|
||||||
|
"connectors.mail",
|
||||||
|
"operations.monitoring",
|
||||||
|
"operations.audit",
|
||||||
|
"operations.backup_restore",
|
||||||
|
"operations.disaster_recovery",
|
||||||
|
}.issubset(infrastructure_ids)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_questionnaire_retains_all_required_fit_dimensions(self) -> None:
|
||||||
|
ids = {
|
||||||
|
item["id"]
|
||||||
|
for answers in self.assessment["questionnaire"].values()
|
||||||
|
for item in answers
|
||||||
|
}
|
||||||
|
self.assertTrue(
|
||||||
|
{
|
||||||
|
"outcome.reference_journey",
|
||||||
|
"scope.users_tenants_organizations",
|
||||||
|
"data.classification",
|
||||||
|
"data.retention",
|
||||||
|
"data.privacy_security_disclosure",
|
||||||
|
"identity.protocols_lifecycle",
|
||||||
|
"integration.protocols_network",
|
||||||
|
"workload.tenants_users_concurrency",
|
||||||
|
"workload.campaign_volume_peaks",
|
||||||
|
"workload.files_jobs_audit_growth_retention",
|
||||||
|
"workload.connector_traffic_batches",
|
||||||
|
"availability.rto_rpo",
|
||||||
|
"hosting.network_constraints",
|
||||||
|
"operations.ownership",
|
||||||
|
"procurement.constraints",
|
||||||
|
}.issubset(ids)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_duplicate_keys_and_sensitive_fields_fail_closed(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
duplicate = Path(directory) / "duplicate.json"
|
||||||
|
duplicate.write_text('{"id": 1, "id": 2}', encoding="utf-8")
|
||||||
|
with self.assertRaisesRegex(AssessmentGenerationError, "Duplicate JSON key"):
|
||||||
|
load_bounded_json(duplicate, label="assessment")
|
||||||
|
|
||||||
|
unsafe = deepcopy(self.assessment)
|
||||||
|
unsafe["password"] = "must-not-render"
|
||||||
|
permissive = deepcopy(self.schema)
|
||||||
|
permissive["additionalProperties"] = True
|
||||||
|
with self.assertRaisesRegex(AssessmentGenerationError, "sensitive field"):
|
||||||
|
validate_report_input(assessment=unsafe, schema=permissive)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -57,18 +57,23 @@ class PackageRegistryReleaseTests(unittest.TestCase):
|
|||||||
self.assertEqual(ARTIFACTS._canonical_sha256(unsigned), digest)
|
self.assertEqual(ARTIFACTS._canonical_sha256(unsigned), digest)
|
||||||
|
|
||||||
def test_full_profile_is_derived_from_the_developer_meta_package(self) -> None:
|
def test_full_profile_is_derived_from_the_developer_meta_package(self) -> None:
|
||||||
|
core_version = tomllib.loads(
|
||||||
|
(ROOT.parent / "govoplan-core/pyproject.toml").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
)["project"]["version"]
|
||||||
selected = PACKAGE_SET.parse_meta_package(
|
selected = PACKAGE_SET.parse_meta_package(
|
||||||
ROOT / "packages/govoplan-meta/pyproject.toml",
|
ROOT / "packages/govoplan-meta/pyproject.toml",
|
||||||
core_version="0.1.18",
|
core_version=core_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
by_name = {item["name"]: item for item in selected}
|
by_name = {item["name"]: item for item in selected}
|
||||||
self.assertIn("govoplan-core", by_name)
|
self.assertIn("govoplan-core", by_name)
|
||||||
self.assertIn("govoplan-records", by_name)
|
self.assertIn("govoplan-records", by_name)
|
||||||
self.assertEqual("0.1.19", by_name["govoplan-tasks"]["version"])
|
self.assertEqual("0.1.20", by_name["govoplan-tasks"]["version"])
|
||||||
|
|
||||||
payload = PACKAGE_SET.generate_package_set(
|
payload = PACKAGE_SET.generate_package_set(
|
||||||
core_version="0.1.18",
|
core_version=core_version,
|
||||||
requirements=ROOT / "requirements-release.txt",
|
requirements=ROOT / "requirements-release.txt",
|
||||||
workspace=ROOT.parent,
|
workspace=ROOT.parent,
|
||||||
profile="full",
|
profile="full",
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate the human capability-fit report from its machine-readable input."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
|
META_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
ASSESSMENT_TOOLS_ROOT = META_ROOT / "tools" / "assessments"
|
||||||
|
RELEASE_TOOLS_ROOT = META_ROOT / "tools" / "release"
|
||||||
|
for tools_root in (ASSESSMENT_TOOLS_ROOT, RELEASE_TOOLS_ROOT):
|
||||||
|
if str(tools_root) not in sys.path:
|
||||||
|
sys.path.insert(0, str(tools_root))
|
||||||
|
|
||||||
|
from govoplan_assessment.report_generator import ( # noqa: E402
|
||||||
|
AssessmentGenerationError,
|
||||||
|
load_bounded_json,
|
||||||
|
render_assessment_markdown,
|
||||||
|
validate_report_input,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_ASSESSMENT = META_ROOT / "docs" / "capability-fit-current.json"
|
||||||
|
DEFAULT_SCHEMA = META_ROOT / "docs" / "capability-fit.schema.json"
|
||||||
|
DEFAULT_OUTPUT = (
|
||||||
|
META_ROOT
|
||||||
|
/ "docs"
|
||||||
|
/ "evidence"
|
||||||
|
/ "snapshots"
|
||||||
|
/ "CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md"
|
||||||
|
)
|
||||||
|
MAX_OUTPUT_BYTES = 16 * 1024 * 1024
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Render a deterministic human report from one capability-fit JSON input."
|
||||||
|
)
|
||||||
|
parser.add_argument("--assessment", type=Path, default=DEFAULT_ASSESSMENT)
|
||||||
|
parser.add_argument("--schema", type=Path, default=DEFAULT_SCHEMA)
|
||||||
|
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
|
||||||
|
parser.add_argument(
|
||||||
|
"--check",
|
||||||
|
action="store_true",
|
||||||
|
help="Fail when the output is missing or differs instead of writing it.",
|
||||||
|
)
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
args = parse_args(argv)
|
||||||
|
try:
|
||||||
|
assessment = load_bounded_json(args.assessment, label="assessment")
|
||||||
|
schema = load_bounded_json(args.schema, label="assessment schema")
|
||||||
|
validate_report_input(assessment=assessment, schema=schema)
|
||||||
|
rendered = render_assessment_markdown(assessment)
|
||||||
|
encoded = rendered.encode("utf-8")
|
||||||
|
if len(encoded) > MAX_OUTPUT_BYTES:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Generated report exceeds the {MAX_OUTPUT_BYTES}-byte output limit"
|
||||||
|
)
|
||||||
|
if args.check:
|
||||||
|
try:
|
||||||
|
current = args.output.read_bytes()
|
||||||
|
except OSError:
|
||||||
|
current = None
|
||||||
|
if current != encoded:
|
||||||
|
print(
|
||||||
|
f"Capability-fit report is stale: {args.output}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 2
|
||||||
|
print(f"Capability-fit report is current: {args.output}")
|
||||||
|
return 0
|
||||||
|
_atomic_write(args.output, encoded)
|
||||||
|
print(f"Generated capability-fit report: {args.output}")
|
||||||
|
return 0
|
||||||
|
except AssessmentGenerationError as exc:
|
||||||
|
print(str(exc), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def _atomic_write(path: Path, content: bytes) -> None:
|
||||||
|
if not path.parent.is_dir():
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Output parent directory does not exist: {path.parent}"
|
||||||
|
)
|
||||||
|
if path.is_symlink():
|
||||||
|
raise AssessmentGenerationError("Output path must not be a symbolic link")
|
||||||
|
descriptor = -1
|
||||||
|
temporary_name = ""
|
||||||
|
try:
|
||||||
|
descriptor, temporary_name = tempfile.mkstemp(
|
||||||
|
prefix=".govoplan-fit-report-",
|
||||||
|
suffix=".tmp",
|
||||||
|
dir=path.parent,
|
||||||
|
)
|
||||||
|
os.fchmod(descriptor, 0o644)
|
||||||
|
with os.fdopen(descriptor, "wb", closefd=True) as handle:
|
||||||
|
descriptor = -1
|
||||||
|
handle.write(content)
|
||||||
|
handle.flush()
|
||||||
|
os.fsync(handle.fileno())
|
||||||
|
os.replace(temporary_name, path)
|
||||||
|
temporary_name = ""
|
||||||
|
except OSError as exc:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Could not write generated report atomically: {exc}"
|
||||||
|
) from exc
|
||||||
|
finally:
|
||||||
|
if descriptor >= 0:
|
||||||
|
os.close(descriptor)
|
||||||
|
if temporary_name:
|
||||||
|
try:
|
||||||
|
os.unlink(temporary_name)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,551 @@
|
|||||||
|
"""Deterministically render one validated capability-fit assessment as Markdown."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Iterable, Mapping, Sequence
|
||||||
|
|
||||||
|
from jsonschema import Draft202012Validator, FormatChecker
|
||||||
|
from jsonschema.exceptions import SchemaError
|
||||||
|
|
||||||
|
|
||||||
|
MAX_ASSESSMENT_BYTES = 16 * 1024 * 1024
|
||||||
|
STATUS_DEFINITIONS = (
|
||||||
|
(
|
||||||
|
"verified",
|
||||||
|
"Implemented and directly exercised by evidence appropriate to the stated scope.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"available_unconfigured",
|
||||||
|
"Implemented with supporting evidence, but not configured and exercised in the target.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"partial",
|
||||||
|
"A useful subset exists, but a material part of the requirement is missing or unproved.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"scaffold",
|
||||||
|
"Contracts or structure exist, but the end-to-end capability is not usable.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"external_system",
|
||||||
|
"The deployment or another system must supply the capability.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"planned",
|
||||||
|
"Only a concept, backlog item, or design direction exists.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"not_fit",
|
||||||
|
"Evidence shows that the assessed composition cannot meet the requirement.",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"not_assessed",
|
||||||
|
"The requirement or target environment is not sufficiently known.",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AssessmentGenerationError(ValueError):
|
||||||
|
"""The assessment cannot be safely validated or rendered."""
|
||||||
|
|
||||||
|
|
||||||
|
def load_bounded_json(path: Path, *, label: str) -> dict[str, Any]:
|
||||||
|
try:
|
||||||
|
size = path.stat().st_size
|
||||||
|
except OSError as exc:
|
||||||
|
raise AssessmentGenerationError(f"Could not inspect {label}: {exc}") from exc
|
||||||
|
if size > MAX_ASSESSMENT_BYTES:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"{label} exceeds the {MAX_ASSESSMENT_BYTES}-byte input limit"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
payload = json.loads(
|
||||||
|
path.read_text(encoding="utf-8"),
|
||||||
|
object_pairs_hook=_unique_object,
|
||||||
|
)
|
||||||
|
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
||||||
|
raise AssessmentGenerationError(f"Could not read {label}: {exc}") from exc
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
raise AssessmentGenerationError(f"{label} must contain one JSON object")
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def validate_report_input(
|
||||||
|
*,
|
||||||
|
assessment: Mapping[str, Any],
|
||||||
|
schema: Mapping[str, Any],
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
Draft202012Validator.check_schema(schema)
|
||||||
|
except SchemaError as exc:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Assessment schema is invalid: {exc.message}"
|
||||||
|
) from exc
|
||||||
|
errors = sorted(
|
||||||
|
Draft202012Validator(
|
||||||
|
schema,
|
||||||
|
format_checker=FormatChecker(),
|
||||||
|
).iter_errors(assessment),
|
||||||
|
key=lambda item: tuple(str(part) for part in item.absolute_path),
|
||||||
|
)
|
||||||
|
if errors:
|
||||||
|
details = "; ".join(
|
||||||
|
f"{_json_path(error.absolute_path)}: {error.message}"
|
||||||
|
for error in errors[:20]
|
||||||
|
)
|
||||||
|
raise AssessmentGenerationError(f"Assessment does not match schema: {details}")
|
||||||
|
_validate_references(assessment)
|
||||||
|
_reject_sensitive_keys(assessment)
|
||||||
|
|
||||||
|
|
||||||
|
def render_assessment_markdown(assessment: Mapping[str, Any]) -> str:
|
||||||
|
"""Return stable Markdown derived only from a validated assessment object."""
|
||||||
|
|
||||||
|
assessment_hash = hashlib.sha256(
|
||||||
|
json.dumps(
|
||||||
|
assessment,
|
||||||
|
sort_keys=True,
|
||||||
|
separators=(",", ":"),
|
||||||
|
ensure_ascii=True,
|
||||||
|
).encode("utf-8")
|
||||||
|
).hexdigest()
|
||||||
|
scope = _mapping(assessment["scope"])
|
||||||
|
release = _mapping(assessment["release"])
|
||||||
|
profile = _mapping(assessment["deployment_profile"])
|
||||||
|
lines = [
|
||||||
|
"# GovOPlaN Capability and IT-Infrastructure Fit Assessment",
|
||||||
|
"",
|
||||||
|
"> Generated from [`capability-fit-current.json`](../../capability-fit-current.json).",
|
||||||
|
"> Edit and validate the machine-readable assessment, then regenerate this file;",
|
||||||
|
"> do not maintain conclusions independently in Markdown.",
|
||||||
|
"",
|
||||||
|
"This is an evidence-based fit assessment, not a production approval or",
|
||||||
|
"security certification. Repository or manifest existence alone never counts",
|
||||||
|
"as an implemented capability. Unknown target requirements remain explicitly",
|
||||||
|
"`not_assessed`.",
|
||||||
|
"",
|
||||||
|
"## Assessment record",
|
||||||
|
"",
|
||||||
|
"| Field | Value |",
|
||||||
|
"| --- | --- |",
|
||||||
|
f"| Assessment ID | `{_cell(assessment['assessment_id'])}` |",
|
||||||
|
f"| Schema version | `govoplan.fit-assessment/{_cell(assessment['schema_version'])}` |",
|
||||||
|
f"| Assessed on | {_cell(assessment['assessed_at'])} |",
|
||||||
|
f"| Scope | {_cell(scope['title'])} |",
|
||||||
|
f"| Release | `{_cell(release['ref'])}` ({_cell(release['kind'])}) |",
|
||||||
|
f"| Meta commit | `{_cell(release['meta_commit'])}` |",
|
||||||
|
f"| Deployment profile | `{_cell(profile['id'])}` · `{_cell(profile['status'])}` |",
|
||||||
|
f"| Configuration packages | {_inline_list(release['configuration_packages'], code=True)} |",
|
||||||
|
f"| Canonical input SHA-256 | `{assessment_hash}` |",
|
||||||
|
"",
|
||||||
|
"## Controlled status vocabulary",
|
||||||
|
"",
|
||||||
|
"| Status | Meaning |",
|
||||||
|
"| --- | --- |",
|
||||||
|
]
|
||||||
|
lines.extend(
|
||||||
|
f"| `{status}` | {_cell(description)} |"
|
||||||
|
for status, description in STATUS_DEFINITIONS
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"## Scope and reference journeys",
|
||||||
|
"",
|
||||||
|
"Reference journeys:",
|
||||||
|
"",
|
||||||
|
*_bullets(scope["reference_journeys"]),
|
||||||
|
"",
|
||||||
|
"Explicitly postponed:",
|
||||||
|
"",
|
||||||
|
*_bullets(scope["postponed"]),
|
||||||
|
"",
|
||||||
|
"## Facts",
|
||||||
|
"",
|
||||||
|
*_bullets(assessment["facts"]),
|
||||||
|
"",
|
||||||
|
"## Decisions",
|
||||||
|
"",
|
||||||
|
*_bullets(assessment["decisions"]),
|
||||||
|
"",
|
||||||
|
"## Assumptions",
|
||||||
|
"",
|
||||||
|
*_bullets(assessment["assumptions"]),
|
||||||
|
"",
|
||||||
|
"## Unresolved decisions",
|
||||||
|
"",
|
||||||
|
*_bullets(assessment["open_questions"]),
|
||||||
|
"",
|
||||||
|
"## Pinned release and composition",
|
||||||
|
"",
|
||||||
|
f"Release reproducible: **{'yes' if release['reproducible'] else 'no'}**.",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
lines.extend(_notes(release.get("notes", [])))
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"| Module | Repository and commit | Manifest version | Enabled | Role |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for module_value in assessment["composition"]:
|
||||||
|
module = _mapping(module_value)
|
||||||
|
lines.append(
|
||||||
|
"| `{}` | `{}` @ `{}` | `{}` | {} | {} |".format(
|
||||||
|
_cell(module["module_id"]),
|
||||||
|
_cell(module["repository"]),
|
||||||
|
_cell(module["commit"]),
|
||||||
|
_cell(module["manifest_version"]),
|
||||||
|
"yes" if module["enabled"] else "no",
|
||||||
|
_cell(module["role"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"## Deployment profile",
|
||||||
|
"",
|
||||||
|
f"Status: `{_cell(profile['status'])}`",
|
||||||
|
"",
|
||||||
|
_text(profile["description"]),
|
||||||
|
"",
|
||||||
|
"Evidence:",
|
||||||
|
"",
|
||||||
|
*_bullets(_evidence_labels(profile["evidence"])),
|
||||||
|
"",
|
||||||
|
"## Recommended scenarios",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for scenario_value in assessment["scenarios"]:
|
||||||
|
scenario = _mapping(scenario_value)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
f"### {_text(scenario['label'])}",
|
||||||
|
"",
|
||||||
|
f"Status: `{_cell(scenario['status'])}`",
|
||||||
|
"",
|
||||||
|
_text(scenario["recommendation"]),
|
||||||
|
"",
|
||||||
|
f"Composition: {_inline_list(scenario['composition'], code=True)}.",
|
||||||
|
"",
|
||||||
|
"Topology:",
|
||||||
|
"",
|
||||||
|
*_bullets(scenario["topology"]),
|
||||||
|
"",
|
||||||
|
"Conditions:",
|
||||||
|
"",
|
||||||
|
*_bullets(scenario["conditions"]),
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
functional_context = _mapping(assessment["functional_context"])
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"## Functional matrix context",
|
||||||
|
"",
|
||||||
|
"### Required modules",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["required_modules"]),
|
||||||
|
"",
|
||||||
|
"### Optional modules",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["optional_modules"]),
|
||||||
|
"",
|
||||||
|
"### External systems and connectors",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["external_systems"]),
|
||||||
|
"",
|
||||||
|
"### Missing contracts",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["missing_contracts"]),
|
||||||
|
"",
|
||||||
|
"### Policy decisions",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["policy_decisions"]),
|
||||||
|
"",
|
||||||
|
"### Manual workarounds",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["manual_workarounds"]),
|
||||||
|
"",
|
||||||
|
"### Blockers",
|
||||||
|
"",
|
||||||
|
*_bullets(functional_context["blockers"]),
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"## Assessment questionnaire",
|
||||||
|
"",
|
||||||
|
"Every required area remains visible even when its target answer is unknown.",
|
||||||
|
"",
|
||||||
|
"| Area | Question | State | Answer | Evidence |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
questionnaire = _mapping(assessment["questionnaire"])
|
||||||
|
for area, answers in questionnaire.items():
|
||||||
|
for answer_value in _sequence(answers):
|
||||||
|
answer = _mapping(answer_value)
|
||||||
|
raw_answer = answer["answer"]
|
||||||
|
answer_text = (
|
||||||
|
_inline_list(raw_answer)
|
||||||
|
if isinstance(raw_answer, list)
|
||||||
|
else _text(raw_answer) if raw_answer is not None else "—"
|
||||||
|
)
|
||||||
|
lines.append(
|
||||||
|
"| {} | {} | `{}` | {} | {} |".format(
|
||||||
|
_cell(area.replace("_", " ").title()),
|
||||||
|
_cell(answer["question"]),
|
||||||
|
_cell(answer["state"]),
|
||||||
|
_cell(answer_text),
|
||||||
|
_cell("; ".join(_evidence_labels(answer["evidence"])) or "—"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.extend(_assessed_matrix("Functional capability matrix", assessment["capabilities"]))
|
||||||
|
lines.extend(_assessed_matrix("Infrastructure matrix", assessment["infrastructure"]))
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"## Data flows and trust boundaries",
|
||||||
|
"",
|
||||||
|
"| Flow | From → to | Data | Trust boundary | Controls |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for flow_value in assessment["data_flows"]:
|
||||||
|
flow = _mapping(flow_value)
|
||||||
|
lines.append(
|
||||||
|
"| `{}` | {} → {} | {} | {} | {} |".format(
|
||||||
|
_cell(flow["id"]),
|
||||||
|
_cell(flow["from"]),
|
||||||
|
_cell(flow["to"]),
|
||||||
|
_cell(_inline_list(flow["data"])),
|
||||||
|
_cell(flow["trust_boundary"]),
|
||||||
|
_cell(_inline_list(flow["controls"])),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"## Risks and residual risks",
|
||||||
|
"",
|
||||||
|
"| Risk | Impact | Treatment | Owner | Residual risk |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for risk_value in assessment["risks"]:
|
||||||
|
risk = _mapping(risk_value)
|
||||||
|
lines.append(
|
||||||
|
"| **{}**<br>{} | {} | {} | {} | {} |".format(
|
||||||
|
_cell(risk["id"]),
|
||||||
|
_cell(risk["statement"]),
|
||||||
|
_cell(risk["impact"]),
|
||||||
|
_cell(risk["treatment"]),
|
||||||
|
_cell(risk["owner"] or "unassigned"),
|
||||||
|
_cell(risk["residual_risk"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"## Recommendations",
|
||||||
|
"",
|
||||||
|
*_bullets(assessment["recommendations"]),
|
||||||
|
"",
|
||||||
|
"## Proof-of-concept and promotion checks",
|
||||||
|
"",
|
||||||
|
*_numbered(assessment["proof_checks"]),
|
||||||
|
"",
|
||||||
|
"## Generation contract",
|
||||||
|
"",
|
||||||
|
"This report is deterministic output from the schema-validated JSON companion.",
|
||||||
|
"The generator rejects duplicate JSON keys, schema drift, secret-bearing field",
|
||||||
|
"names, stale checked-in output, and oversized inputs. A new assessment or",
|
||||||
|
"release changes the canonical input hash and requires review of the affected",
|
||||||
|
"evidence and conclusions through the release-aware reassessment tool.",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _assessed_matrix(title: str, values: object) -> list[str]:
|
||||||
|
lines = [
|
||||||
|
"",
|
||||||
|
f"## {title}",
|
||||||
|
"",
|
||||||
|
"| Requirement | Status | Evidence | Conditions and gaps | Recommendation and proof |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
for item_value in _sequence(values):
|
||||||
|
item = _mapping(item_value)
|
||||||
|
conditions = [f"Condition: {value}" for value in item["conditions"]]
|
||||||
|
gaps = [f"Gap: {value}" for value in item["gaps"]]
|
||||||
|
risks = [f"Risk: {value}" for value in item["risks"]]
|
||||||
|
lines.append(
|
||||||
|
"| **{}**<br>{} | `{}` | {} | {} | {}<br>**Proof:** {} |".format(
|
||||||
|
_cell(item["id"]),
|
||||||
|
_cell(item["requirement"]),
|
||||||
|
_cell(item["status"]),
|
||||||
|
_cell("; ".join(_evidence_labels(item["evidence"])) or "Explicit absence of evidence"),
|
||||||
|
_cell("; ".join([*conditions, *gaps, *risks]) or "—"),
|
||||||
|
_cell(item["recommendation"] or "—"),
|
||||||
|
_cell(item["proof_check"] or "—"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def _evidence_labels(values: object) -> list[str]:
|
||||||
|
labels: list[str] = []
|
||||||
|
for value in _sequence(values):
|
||||||
|
item = _mapping(value)
|
||||||
|
label = f"{item['kind']}/{item['scope']}: {item['locator']}"
|
||||||
|
if item.get("note"):
|
||||||
|
label += f" ({item['note']})"
|
||||||
|
labels.append(label)
|
||||||
|
return labels
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_object(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
|
||||||
|
result: dict[str, Any] = {}
|
||||||
|
for key, value in pairs:
|
||||||
|
if key in result:
|
||||||
|
raise AssessmentGenerationError(f"Duplicate JSON key: {key!r}")
|
||||||
|
result[key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _reject_sensitive_keys(value: object, path: tuple[str, ...] = ()) -> None:
|
||||||
|
forbidden = {
|
||||||
|
"access_token",
|
||||||
|
"api_key",
|
||||||
|
"credential_value",
|
||||||
|
"password",
|
||||||
|
"private_key",
|
||||||
|
"refresh_token",
|
||||||
|
"secret",
|
||||||
|
}
|
||||||
|
if isinstance(value, Mapping):
|
||||||
|
for key, nested in value.items():
|
||||||
|
normalized = str(key).strip().casefold()
|
||||||
|
if normalized in forbidden:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Assessment contains forbidden sensitive field {_json_path((*path, str(key)))}"
|
||||||
|
)
|
||||||
|
_reject_sensitive_keys(nested, (*path, str(key)))
|
||||||
|
elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
|
||||||
|
for index, nested in enumerate(value):
|
||||||
|
_reject_sensitive_keys(nested, (*path, str(index)))
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_references(assessment: Mapping[str, Any]) -> None:
|
||||||
|
composition = [_mapping(item) for item in _sequence(assessment["composition"])]
|
||||||
|
module_ids = [str(item["module_id"]) for item in composition]
|
||||||
|
if len(module_ids) != len(set(module_ids)):
|
||||||
|
raise AssessmentGenerationError("Composition contains duplicate module IDs")
|
||||||
|
modules = {str(item["module_id"]): item for item in composition}
|
||||||
|
context = _mapping(assessment["functional_context"])
|
||||||
|
required = {str(item) for item in _sequence(context["required_modules"])}
|
||||||
|
optional = {str(item) for item in _sequence(context["optional_modules"])}
|
||||||
|
unknown_context = (required | optional) - set(modules)
|
||||||
|
if unknown_context:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
"Functional context references unknown modules: "
|
||||||
|
+ ", ".join(sorted(unknown_context))
|
||||||
|
)
|
||||||
|
if required & optional:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
"Functional context cannot mark a module both required and optional"
|
||||||
|
)
|
||||||
|
for scenario_value in _sequence(assessment["scenarios"]):
|
||||||
|
scenario = _mapping(scenario_value)
|
||||||
|
referenced = {str(item) for item in _sequence(scenario["composition"])}
|
||||||
|
unknown = referenced - set(modules)
|
||||||
|
if unknown:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Scenario {scenario['id']!r} references unknown modules: "
|
||||||
|
+ ", ".join(sorted(unknown))
|
||||||
|
)
|
||||||
|
disabled = sorted(
|
||||||
|
module_id
|
||||||
|
for module_id in referenced
|
||||||
|
if not bool(modules[module_id]["enabled"])
|
||||||
|
)
|
||||||
|
if disabled:
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Scenario {scenario['id']!r} references disabled modules: "
|
||||||
|
+ ", ".join(disabled)
|
||||||
|
)
|
||||||
|
for collection in ("capabilities", "infrastructure", "data_flows", "risks"):
|
||||||
|
identifiers = [
|
||||||
|
str(_mapping(item)["id"])
|
||||||
|
for item in _sequence(assessment[collection])
|
||||||
|
]
|
||||||
|
if len(identifiers) != len(set(identifiers)):
|
||||||
|
raise AssessmentGenerationError(
|
||||||
|
f"Assessment contains duplicate {collection} IDs"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _mapping(value: object) -> Mapping[str, Any]:
|
||||||
|
if not isinstance(value, Mapping):
|
||||||
|
raise AssessmentGenerationError("Validated assessment contains a non-object value")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _sequence(value: object) -> Sequence[Any]:
|
||||||
|
if not isinstance(value, Sequence) or isinstance(value, (str, bytes)):
|
||||||
|
raise AssessmentGenerationError("Validated assessment contains a non-list value")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _text(value: object) -> str:
|
||||||
|
return str(value).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def _cell(value: object) -> str:
|
||||||
|
return _text(value).replace("|", "\\|").replace("\r", " ").replace("\n", " ")
|
||||||
|
|
||||||
|
|
||||||
|
def _inline_list(values: object, *, code: bool = False) -> str:
|
||||||
|
items = [_text(item) for item in _sequence(values)]
|
||||||
|
if not items:
|
||||||
|
return "none"
|
||||||
|
if code:
|
||||||
|
return ", ".join(f"`{_cell(item)}`" for item in items)
|
||||||
|
return "; ".join(items)
|
||||||
|
|
||||||
|
|
||||||
|
def _bullets(values: object) -> list[str]:
|
||||||
|
items = [_text(item) for item in _sequence(values)]
|
||||||
|
return [f"- {item}" for item in items] or ["- None recorded."]
|
||||||
|
|
||||||
|
|
||||||
|
def _numbered(values: object) -> list[str]:
|
||||||
|
return [f"{index}. {_text(item)}" for index, item in enumerate(_sequence(values), 1)]
|
||||||
|
|
||||||
|
|
||||||
|
def _notes(values: object) -> list[str]:
|
||||||
|
items = _bullets(values)
|
||||||
|
return ["Release notes:", "", *items]
|
||||||
|
|
||||||
|
|
||||||
|
def _json_path(parts: Iterable[object]) -> str:
|
||||||
|
suffix = "".join(f"[{part}]" if str(part).isdigit() else f".{part}" for part in parts)
|
||||||
|
return f"${suffix}"
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"AssessmentGenerationError",
|
||||||
|
"MAX_ASSESSMENT_BYTES",
|
||||||
|
"load_bounded_json",
|
||||||
|
"render_assessment_markdown",
|
||||||
|
"validate_report_input",
|
||||||
|
)
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Require DSAR coverage or a reviewed no-store rationale for every module."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import importlib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
META_ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
EXEMPTIONS_PATH = Path(__file__).with_name("dsar-coverage-exemptions.json")
|
||||||
|
REPORT_PATH = (
|
||||||
|
META_ROOT
|
||||||
|
/ "docs"
|
||||||
|
/ "evidence"
|
||||||
|
/ "snapshots"
|
||||||
|
/ "DSAR_PROVIDER_COVERAGE.generated.md"
|
||||||
|
)
|
||||||
|
MODULE_NAME_PATTERN = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*")
|
||||||
|
REQUIRED_DOCUMENTATION_TYPES = frozenset({"admin"})
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class CoverageRow:
|
||||||
|
module_id: str
|
||||||
|
repository: str
|
||||||
|
migration_owned: bool
|
||||||
|
capability: str | None
|
||||||
|
rationale: str
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument(
|
||||||
|
"--workspace-root",
|
||||||
|
type=Path,
|
||||||
|
default=None,
|
||||||
|
help="Directory containing GovOPlaN repositories.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--render",
|
||||||
|
action="store_true",
|
||||||
|
help="Print the current matrix instead of comparing the checked-in report.",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
catalog = json.loads((META_ROOT / "repositories.json").read_text(encoding="utf-8"))
|
||||||
|
workspace_root = (args.workspace_root or Path(catalog["default_parent"])).resolve()
|
||||||
|
exemptions = _exemptions()
|
||||||
|
manifests, load_errors = _load_manifests(
|
||||||
|
workspace_root=workspace_root,
|
||||||
|
repositories=tuple(catalog["repositories"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
errors = list(load_errors)
|
||||||
|
rows: list[CoverageRow] = []
|
||||||
|
manifest_ids = {manifest.id for _, manifest in manifests}
|
||||||
|
stale_exemptions = sorted(set(exemptions) - manifest_ids)
|
||||||
|
if stale_exemptions:
|
||||||
|
errors.append(
|
||||||
|
"DSAR coverage exemptions reference unknown modules: "
|
||||||
|
+ ", ".join(stale_exemptions)
|
||||||
|
)
|
||||||
|
|
||||||
|
for repository, manifest in manifests:
|
||||||
|
expected = f"privacy.dsar.{manifest.id}"
|
||||||
|
provided = {
|
||||||
|
item.name
|
||||||
|
for item in manifest.provides_interfaces
|
||||||
|
if item.name.startswith("privacy.dsar.")
|
||||||
|
}
|
||||||
|
factories = {
|
||||||
|
name
|
||||||
|
for name in manifest.capability_factories
|
||||||
|
if name.startswith("privacy.dsar.")
|
||||||
|
}
|
||||||
|
migration_owned = manifest.migration_spec is not None
|
||||||
|
rationale = exemptions.get(manifest.id)
|
||||||
|
|
||||||
|
if provided != factories:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: DSAR interface/factory mismatch: "
|
||||||
|
f"interfaces={sorted(provided)!r}, factories={sorted(factories)!r}"
|
||||||
|
)
|
||||||
|
if provided and provided != {expected}:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: expected only {expected!r}, found {sorted(provided)!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
capability = (
|
||||||
|
expected if expected in provided and expected in factories else None
|
||||||
|
)
|
||||||
|
if migration_owned and capability is None:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: migration-owning module {manifest.id!r} must provide "
|
||||||
|
f"and register {expected!r}"
|
||||||
|
)
|
||||||
|
if migration_owned and rationale is not None:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: migration-owning module {manifest.id!r} cannot use a "
|
||||||
|
"no-store DSAR exemption"
|
||||||
|
)
|
||||||
|
if not migration_owned and capability is None and rationale is None:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: module {manifest.id!r} needs a DSAR provider or an "
|
||||||
|
"explicit reviewed no-store rationale"
|
||||||
|
)
|
||||||
|
if capability is not None and rationale is not None:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: module {manifest.id!r} has both DSAR coverage and a "
|
||||||
|
"stale exemption"
|
||||||
|
)
|
||||||
|
if capability is not None:
|
||||||
|
if capability not in manifest.capability_documentation:
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: {capability!r} lacks capability documentation"
|
||||||
|
)
|
||||||
|
matching_topics = tuple(
|
||||||
|
topic
|
||||||
|
for topic in manifest.documentation
|
||||||
|
if "data-subject-request" in topic.id
|
||||||
|
)
|
||||||
|
if not matching_topics or not any(
|
||||||
|
REQUIRED_DOCUMENTATION_TYPES.issubset(topic.documentation_types)
|
||||||
|
for topic in matching_topics
|
||||||
|
):
|
||||||
|
errors.append(
|
||||||
|
f"{repository}: DSAR coverage needs a static administrator "
|
||||||
|
"data-subject-requests DocumentationTopic"
|
||||||
|
)
|
||||||
|
|
||||||
|
rows.append(
|
||||||
|
CoverageRow(
|
||||||
|
module_id=manifest.id,
|
||||||
|
repository=repository,
|
||||||
|
migration_owned=migration_owned,
|
||||||
|
capability=capability,
|
||||||
|
rationale=(
|
||||||
|
f"Provider `{capability}` is registered and documented."
|
||||||
|
if capability
|
||||||
|
else rationale or "MISSING"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
report = _report(rows)
|
||||||
|
if args.render:
|
||||||
|
print(report, end="")
|
||||||
|
elif not REPORT_PATH.is_file():
|
||||||
|
errors.append(f"DSAR coverage report is missing: {REPORT_PATH}")
|
||||||
|
elif REPORT_PATH.read_text(encoding="utf-8") != report:
|
||||||
|
errors.append(
|
||||||
|
"DSAR coverage report is stale; review changes and replace it with "
|
||||||
|
"the output of tools/checks/check-dsar-coverage.py --render"
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print("\n".join(errors), file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
provider_count = sum(row.capability is not None for row in rows)
|
||||||
|
print(
|
||||||
|
"DSAR coverage check passed: "
|
||||||
|
f"{provider_count} providers, {len(rows) - provider_count} reviewed "
|
||||||
|
f"no-store rationales, {len(rows)} active modules."
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def _exemptions() -> dict[str, str]:
|
||||||
|
values = json.loads(EXEMPTIONS_PATH.read_text(encoding="utf-8"))
|
||||||
|
if not isinstance(values, dict) or any(
|
||||||
|
not isinstance(key, str) or not isinstance(value, str) or not value.strip()
|
||||||
|
for key, value in values.items()
|
||||||
|
):
|
||||||
|
raise ValueError("DSAR coverage exemptions must be non-empty string mappings.")
|
||||||
|
return {key: value.strip() for key, value in values.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def _load_manifests(*, workspace_root: Path, repositories: tuple[dict, ...]):
|
||||||
|
sources: list[Path] = []
|
||||||
|
candidates: list[tuple[str, Path, Path]] = []
|
||||||
|
for repository in repositories:
|
||||||
|
source = workspace_root / repository["path"] / "src"
|
||||||
|
if not source.is_dir():
|
||||||
|
continue
|
||||||
|
sources.append(source)
|
||||||
|
candidates.extend(
|
||||||
|
(repository["name"], source, path)
|
||||||
|
for path in sorted(source.glob("*/backend/manifest.py"))
|
||||||
|
)
|
||||||
|
core_source = workspace_root / "govoplan-core" / "src"
|
||||||
|
sys.path[:0] = [
|
||||||
|
str(core_source),
|
||||||
|
*(str(source) for source in sources if source != core_source),
|
||||||
|
]
|
||||||
|
|
||||||
|
manifests = []
|
||||||
|
errors = []
|
||||||
|
for repository, source, path in candidates:
|
||||||
|
module_name = ".".join(path.relative_to(source).with_suffix("").parts)
|
||||||
|
if MODULE_NAME_PATTERN.fullmatch(module_name) is None:
|
||||||
|
errors.append(f"{repository}: unsafe manifest module name {module_name!r}")
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
manifests.append((repository, module.get_manifest()))
|
||||||
|
except Exception as exc: # pragma: no cover - emitted as check evidence
|
||||||
|
errors.append(f"{repository}: could not load {module_name}: {exc}")
|
||||||
|
return manifests, errors
|
||||||
|
|
||||||
|
|
||||||
|
def _report(rows: list[CoverageRow]) -> str:
|
||||||
|
ordered = sorted(rows, key=lambda row: row.module_id)
|
||||||
|
providers = sum(row.capability is not None for row in ordered)
|
||||||
|
lines = [
|
||||||
|
"# DSAR Provider Coverage",
|
||||||
|
"",
|
||||||
|
"This generated matrix is enforced by `tools/checks/check-dsar-coverage.py`.",
|
||||||
|
"A migration-owning module must register and document its canonical DSAR provider.",
|
||||||
|
"Every other active module requires a reviewed explanation of why it owns no",
|
||||||
|
"persistent subject-data store. Adding a migration invalidates that explanation.",
|
||||||
|
"",
|
||||||
|
f"- Active modules: {len(ordered)}",
|
||||||
|
f"- Registered and documented DSAR providers: {providers}",
|
||||||
|
f"- Reviewed no-store rationales: {len(ordered) - providers}",
|
||||||
|
"- Unexplained coverage gaps: 0",
|
||||||
|
"",
|
||||||
|
"| Module | Repository | Persistence | Coverage | Rationale |",
|
||||||
|
"| --- | --- | --- | --- | --- |",
|
||||||
|
]
|
||||||
|
for row in ordered:
|
||||||
|
lines.append(
|
||||||
|
"| "
|
||||||
|
+ " | ".join(
|
||||||
|
(
|
||||||
|
f"`{row.module_id}`",
|
||||||
|
f"`{row.repository}`",
|
||||||
|
"Migration-owned" if row.migration_owned else "No module migration",
|
||||||
|
"Provider" if row.capability else "Reviewed no-store rationale",
|
||||||
|
row.rationale.replace("|", "\\|"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
+ " |"
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
(
|
||||||
|
"",
|
||||||
|
"Provider search, export minimization, retention, and erasure behavior remains",
|
||||||
|
"documented and tested by each owning module. This matrix verifies adoption and",
|
||||||
|
"ownership coverage; Core continues to test disabled providers, partial failure,",
|
||||||
|
"retry, authorization evidence, and horizontally coordinated execution.",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -38,6 +38,7 @@ cd "$ROOT"
|
|||||||
GOVOPLAN_CORE_ROOT="$ROOT" PYTHON="$PYTHON" CHECK_TESTCLIENT_DEPRECATIONS=1 bash "$META_ROOT/tools/checks/check-dependency-hygiene.sh"
|
GOVOPLAN_CORE_ROOT="$ROOT" PYTHON="$PYTHON" CHECK_TESTCLIENT_DEPRECATIONS=1 bash "$META_ROOT/tools/checks/check-dependency-hygiene.sh"
|
||||||
"$PYTHON" "$META_ROOT/tools/checks/check-contracts.py" --no-impact
|
"$PYTHON" "$META_ROOT/tools/checks/check-contracts.py" --no-impact
|
||||||
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$META_ROOT/tools/checks/check-manifest-shapes.py" --require-architecture
|
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$META_ROOT/tools/checks/check-manifest-shapes.py" --require-architecture
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$META_ROOT/tools/checks/check-dsar-coverage.py"
|
||||||
|
|
||||||
cd "$META_ROOT"
|
cd "$META_ROOT"
|
||||||
"$PYTHON" tools/inventory/platform-interface-inventory.py --strict-declarations --strict-endpoints
|
"$PYTHON" tools/inventory/platform-interface-inventory.py --strict-declarations --strict-endpoints
|
||||||
@@ -46,6 +47,8 @@ cd "$META_ROOT"
|
|||||||
"$PYTHON" -m unittest tests.test_module_package_workflows tests.test_package_registry_release
|
"$PYTHON" -m unittest tests.test_module_package_workflows tests.test_package_registry_release
|
||||||
"$PYTHON" -m unittest tests.test_deployment_installer
|
"$PYTHON" -m unittest tests.test_deployment_installer
|
||||||
"$PYTHON" -m unittest tests.test_capability_fit_evidence
|
"$PYTHON" -m unittest tests.test_capability_fit_evidence
|
||||||
|
"$PYTHON" -m unittest tests.test_capability_fit_generation tests.test_capability_fit_review
|
||||||
|
"$PYTHON" tools/assessments/generate-capability-fit-report.py --check
|
||||||
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
||||||
"$PYTHON" -m unittest tests.test_institutional_governance_journey
|
"$PYTHON" -m unittest tests.test_institutional_governance_journey
|
||||||
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
||||||
@@ -112,6 +115,7 @@ PY
|
|||||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-approvals/tests
|
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-approvals/tests
|
||||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-identity-trust/tests
|
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-identity-trust/tests
|
||||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-encryption/tests
|
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-encryption/tests
|
||||||
|
"$PYTHON" -m pytest -q /mnt/DATA/git/govoplan-wiki/tests
|
||||||
"$PYTHON" -m pytest -q /mnt/DATA/git/govoplan-campaign/tests/test_approval_gate.py
|
"$PYTHON" -m pytest -q /mnt/DATA/git/govoplan-campaign/tests/test_approval_gate.py
|
||||||
"$PYTHON" "$META_ROOT/tools/checks/check-datasource-composition.py"
|
"$PYTHON" "$META_ROOT/tools/checks/check-datasource-composition.py"
|
||||||
"$PYTHON" "$META_ROOT/tools/checks/check-sanctions-screening-composition.py"
|
"$PYTHON" "$META_ROOT/tools/checks/check-sanctions-screening-composition.py"
|
||||||
@@ -155,3 +159,8 @@ cd /mnt/DATA/git/govoplan-campaign/webui
|
|||||||
"$NPM" run test:policy-ui
|
"$NPM" run test:policy-ui
|
||||||
"$NPM" run test:template-preview
|
"$NPM" run test:template-preview
|
||||||
"$NPM" run test:accessibility-contract
|
"$NPM" run test:accessibility-contract
|
||||||
|
"$NPM" run test:campaign-collaboration
|
||||||
|
"$NPM" run test:campaign-work
|
||||||
|
|
||||||
|
cd /mnt/DATA/git/govoplan-wiki/webui
|
||||||
|
"$NPM" run test:interface-pattern
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ cd "$ROOT"
|
|||||||
"$PYTHON" "$META_ROOT/tools/checks/check_dependency_boundaries.py"
|
"$PYTHON" "$META_ROOT/tools/checks/check_dependency_boundaries.py"
|
||||||
|
|
||||||
cd "$META_ROOT"
|
cd "$META_ROOT"
|
||||||
|
"$PYTHON" -m unittest tests.test_capability_fit_generation
|
||||||
|
"$PYTHON" tools/assessments/generate-capability-fit-report.py --check
|
||||||
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
||||||
PYTHONPATH="$META_ROOT/../govoplan-portal/src:$META_ROOT/../govoplan-forms/src:$META_ROOT/../govoplan-forms-runtime/src:$META_ROOT/../govoplan-cases/src:$ROOT/src${PYTHONPATH:+:$PYTHONPATH}" \
|
PYTHONPATH="$META_ROOT/../govoplan-portal/src:$META_ROOT/../govoplan-forms/src:$META_ROOT/../govoplan-forms-runtime/src:$META_ROOT/../govoplan-cases/src:$ROOT/src${PYTHONPATH:+:$PYTHONPATH}" \
|
||||||
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ cd "$WORK_ROOT/govoplan-campaign/webui"
|
|||||||
"$NPM" run test:policy-ui
|
"$NPM" run test:policy-ui
|
||||||
"$NPM" run test:template-preview
|
"$NPM" run test:template-preview
|
||||||
"$NPM" run test:import-utils
|
"$NPM" run test:import-utils
|
||||||
|
"$NPM" run test:campaign-collaboration
|
||||||
|
"$NPM" run test:campaign-work
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Release integration check passed."
|
echo "Release integration check passed."
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"assets": "Contract-only module: asset persistence and lifecycle APIs are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"booking": "Contract-only module: booking persistence and reservation workflows are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"certificates": "Contract-only module: certificate issuance and revocation persistence are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"consultation": "Contract-only module: consultation submissions and evaluation persistence are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"contracts": "Contract-only module: contract, amendment, and obligation persistence are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"evaluation": "Contract-only module: evaluation runs, responses, and scores are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"facilities": "Contract-only module: facility and maintenance persistence are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"grants": "Contract-only module: grant applications, awards, and monitoring are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"inspections": "Contract-only module: inspections, findings, and measures are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"learning": "Contract-only module: learning offers, enrollment, and completion are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"ops": "Projection-only module: Ops reads bounded platform and provider status; durable recovery evidence remains owned by Core and domain modules.",
|
||||||
|
"permits": "Contract-only module: permit applications, assessments, and decisions are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"portal": "Projection-only module: Portal stores no applicant records; Services, Forms Runtime, Cases, and Postbox own and export authoritative subject data.",
|
||||||
|
"procurement": "Contract-only module: procurement procedures, tenders, and awards are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"resources": "Contract-only module: resource catalog and allocation persistence are not implemented; reassess before adding a migration-owned store.",
|
||||||
|
"rest": "Transport-only module: REST binds explicitly published functions and owns no domain or subject-data store.",
|
||||||
|
"soap": "Transport-only module: SOAP binds explicitly published operations and owns no domain or subject-data store.",
|
||||||
|
"tenancy": "Orchestration module: tenant lifecycle and settings use Core-owned storage; Access covers account and membership subject data.",
|
||||||
|
"transparency": "Contract-only module: requests, disclosure reviews, and publications are not persisted; reassess before adding a migration-owned store.",
|
||||||
|
"workflow": "Presentation-only module: Workflow edits and projects Workflow Engine state; Workflow Engine owns persistence and DSAR coverage."
|
||||||
|
}
|
||||||
@@ -280,6 +280,13 @@
|
|||||||
"rationale": "The module WebUI constructs this endpoint through a mounted router prefix, generic action, or provider path.",
|
"rationale": "The module WebUI constructs this endpoint through a mounted router prefix, generic action, or provider path.",
|
||||||
"repository": "govoplan-audit"
|
"repository": "govoplan-audit"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "intentionally_headless",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/admin/audit/evidence-bundles/{}",
|
||||||
|
"rationale": "Evidence export clients can poll the persisted request/result lifecycle before downloading; the current synchronous administration action downloads ready bundles directly.",
|
||||||
|
"repository": "govoplan-audit"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "ui_reachable",
|
"category": "ui_reachable",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -469,6 +476,188 @@
|
|||||||
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
"repository": "govoplan-connectors"
|
"repository": "govoplan-connectors"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/governed/configurations",
|
||||||
|
"rationale": "The Connector governance administration page lists tenant configurations through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/governed/configurations",
|
||||||
|
"rationale": "The Connector governance administration page creates tenant configurations through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/connectors/governed/configurations/{}",
|
||||||
|
"rationale": "The Connector governance administration page constructs this revision-guarded URL from the selected configuration identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/governed/configurations/{}/dry-runs",
|
||||||
|
"rationale": "The Connector governance administration page constructs this bounded dry-run URL from the selected configuration identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/governed/configurations/{}/simulations",
|
||||||
|
"rationale": "The Connector governance administration page constructs this bounded simulation URL from the selected configuration identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/governed/definitions",
|
||||||
|
"rationale": "The Connector governance administration page lists governed connector definitions through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/governed/definitions",
|
||||||
|
"rationale": "The Connector governance administration page publishes immutable connector-definition revisions through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/governed/runs",
|
||||||
|
"rationale": "The Connector governance administration page lists dry-run and simulation evidence through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/governed/runs/{}/review",
|
||||||
|
"rationale": "The Connector governance administration page constructs this review-decision URL from the selected run identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/knowledge/profiles",
|
||||||
|
"rationale": "The External knowledge administration page lists MediaWiki and BlueSpice profiles through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/knowledge/profiles",
|
||||||
|
"rationale": "The External knowledge administration page creates a mapped, ACL-governed knowledge profile through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}",
|
||||||
|
"rationale": "The External knowledge administration page constructs this revision-guarded profile URL from the selected profile identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}/discover",
|
||||||
|
"rationale": "The External knowledge administration page discovers product, version, capabilities, namespaces, and diagnostics through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}/migration-dry-runs",
|
||||||
|
"rationale": "The External knowledge administration page runs a bounded, non-writing native-Wiki migration preview through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}/objects",
|
||||||
|
"rationale": "The External knowledge administration page lists synchronized, identity-stable knowledge snapshots through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}/pages/{}/publish",
|
||||||
|
"rationale": "The External knowledge administration page constructs this revision-checked external publication URL from the selected profile and page identifiers.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/knowledge/profiles/{}/sync",
|
||||||
|
"rationale": "The External knowledge administration page runs keyed full backfills and recent-change deltas through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/knowledge/runs",
|
||||||
|
"rationale": "The External knowledge administration page lists synchronization, migration-preview, and publication evidence through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/service-desk/profiles",
|
||||||
|
"rationale": "The External service desk administration page lists Znuny/OTRS profiles through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/service-desk/profiles",
|
||||||
|
"rationale": "The External service desk administration page creates route-, queue-, field-, authority-, and ACL-governed profiles through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/connectors/service-desk/profiles/{}",
|
||||||
|
"rationale": "The External service desk administration page constructs this revision-guarded profile URL from the selected profile identifier.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/service-desk/profiles/{}/discover",
|
||||||
|
"rationale": "The External service desk administration page discovers product, version, maturity, capabilities, route health, and diagnostics through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/service-desk/profiles/{}/objects",
|
||||||
|
"rationale": "The External service desk administration page lists synchronized identity-stable, ACL-governed external ticket projections through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/service-desk/profiles/{}/sync",
|
||||||
|
"rationale": "The External service desk administration page runs keyed bounded full and delta synchronization through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/service-desk/profiles/{}/tickets/{}/update",
|
||||||
|
"rationale": "The External service desk ticket list opens a separated revision-checked governed update dialog and constructs this URL from stable profile and provider ticket identifiers.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/connectors/service-desk/runs",
|
||||||
|
"rationale": "The External service desk administration page lists synchronization and external-mutation recovery evidence through this endpoint.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "intentionally_headless",
|
"category": "intentionally_headless",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -504,6 +693,13 @@
|
|||||||
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
"repository": "govoplan-connectors"
|
"repository": "govoplan-connectors"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "intentionally_headless",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/tabular-sources/files",
|
||||||
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "intentionally_headless",
|
"category": "intentionally_headless",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
@@ -511,6 +707,13 @@
|
|||||||
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
"repository": "govoplan-connectors"
|
"repository": "govoplan-connectors"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "intentionally_headless",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/tabular-sources/sql",
|
||||||
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "intentionally_headless",
|
"category": "intentionally_headless",
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
@@ -525,6 +728,13 @@
|
|||||||
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
"repository": "govoplan-connectors"
|
"repository": "govoplan-connectors"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "intentionally_headless",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/connectors/tabular-sources/{}/refresh",
|
||||||
|
"rationale": "This capability-first module intentionally exposes a headless API for other modules and integrations.",
|
||||||
|
"repository": "govoplan-connectors"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "public_integration",
|
"category": "public_integration",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -693,6 +903,90 @@
|
|||||||
"rationale": "The module WebUI constructs this endpoint through a mounted router prefix, generic action, or provider path.",
|
"rationale": "The module WebUI constructs this endpoint through a mounted router prefix, generic action, or provider path.",
|
||||||
"repository": "govoplan-docs"
|
"repository": "govoplan-docs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/entries",
|
||||||
|
"rationale": "The Docs semantic authoring UI calls this mounted sub-router through the /docs prefix, which static endpoint matching cannot compose.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/semantic/entries",
|
||||||
|
"rationale": "The Docs semantic authoring UI calls this mounted sub-router through the /docs prefix, which static endpoint matching cannot compose.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/entries/{}",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs the entry identifier dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/semantic/entries/{}",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs the entry identifier dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/entries/{}/history",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs immutable history paths dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/semantic/entries/{}/publish",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs lifecycle action paths dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/semantic/entries/{}/retire",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs lifecycle action paths dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/semantic/entries/{}/supersede",
|
||||||
|
"rationale": "The Docs semantic authoring UI constructs lifecycle action paths dynamically on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/export",
|
||||||
|
"rationale": "The Docs semantic authoring UI calls tenant export on the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/policy",
|
||||||
|
"rationale": "The Docs semantic authoring UI reads publication policy through the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "PUT",
|
||||||
|
"path": "/semantic/policy",
|
||||||
|
"rationale": "The Docs semantic authoring UI saves publication policy through the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/semantic/subjects",
|
||||||
|
"rationale": "The Docs semantic authoring UI discovers authorized module subjects through the mounted semantic sub-router.",
|
||||||
|
"repository": "govoplan-docs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "ui_reachable",
|
"category": "ui_reachable",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
@@ -924,6 +1218,20 @@
|
|||||||
"rationale": "Retained compatibility endpoint; current module surfaces use the replacement contract.",
|
"rationale": "Retained compatibility endpoint; current module surfaces use the replacement contract.",
|
||||||
"repository": "govoplan-identity"
|
"repository": "govoplan-identity"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/identity/identities/{}/activate",
|
||||||
|
"rationale": "The Identity administration workspace constructs this explicit lifecycle-action URL from the selected identity identifier.",
|
||||||
|
"repository": "govoplan-identity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ui_reachable",
|
||||||
|
"method": "POST",
|
||||||
|
"path": "/identity/identities/{}/deactivate",
|
||||||
|
"rationale": "The Identity administration workspace constructs this explicit lifecycle-action URL from the selected identity identifier.",
|
||||||
|
"repository": "govoplan-identity"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"category": "intentionally_headless",
|
"category": "intentionally_headless",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
@@ -1820,68 +2128,60 @@
|
|||||||
"repository": "govoplan-campaign"
|
"repository": "govoplan-campaign"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/relationships",
|
"path": "/relationships",
|
||||||
"rationale": "IDM documents typed relationships but its current administration page exposes function assignments only.",
|
"rationale": "The IDM relationship administration grid lists effective-dated identity relationships and their lifecycle state.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"path": "/relationships",
|
"path": "/relationships",
|
||||||
"rationale": "IDM documents typed relationships but its current administration page exposes function assignments only.",
|
"rationale": "Authorized IDM administrators create relationships through the searchable relationship editor.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "PATCH",
|
"method": "PATCH",
|
||||||
"path": "/relationships/{}",
|
"path": "/relationships/{}",
|
||||||
"rationale": "IDM documents typed relationships but its current administration page exposes function assignments only.",
|
"rationale": "Authorized IDM administrators update a loaded relationship with its optimistic revision.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"path": "/relationships/{}/revoke",
|
"path": "/relationships/{}/revoke",
|
||||||
"rationale": "IDM documents typed relationships but its current administration page exposes function assignments only.",
|
"rationale": "The IDM relationship grid exposes a separated destructive action with mandatory reason and confirmation.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/typed-groups",
|
"path": "/typed-groups",
|
||||||
"rationale": "IDM documents typed groups but its current administration page exposes function assignments only.",
|
"rationale": "The IDM typed-group administration grid lists active and optionally inactive groups.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"path": "/typed-groups",
|
"path": "/typed-groups",
|
||||||
"rationale": "IDM documents typed groups but its current administration page exposes function assignments only.",
|
"rationale": "Authorized IDM administrators create typed groups through the group editor.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "PATCH",
|
"method": "PATCH",
|
||||||
"path": "/typed-groups/{}",
|
"path": "/typed-groups/{}",
|
||||||
"rationale": "IDM documents typed groups but its current administration page exposes function assignments only.",
|
"rationale": "Authorized IDM administrators edit group metadata, lifecycle state, and provenance with optimistic revision checks.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "missing_ui",
|
"category": "ui_reachable",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/typed-groups/{}/memberships",
|
"path": "/typed-groups/{}/memberships",
|
||||||
"rationale": "IDM lacks the typed-group membership explanation surface for this existing API.",
|
"rationale": "The IDM membership inspector resolves a group at a selected time and shows included and excluded decisions.",
|
||||||
"repository": "govoplan-idm",
|
"repository": "govoplan-idm"
|
||||||
"tracking_issue": "https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/11"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"category": "intentionally_headless",
|
"category": "intentionally_headless",
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ const labelAttributes = new Set([
|
|||||||
"title"
|
"title"
|
||||||
]);
|
]);
|
||||||
const helpAttributes = new Set([
|
const helpAttributes = new Set([
|
||||||
|
"data-help-context-id",
|
||||||
"description",
|
"description",
|
||||||
"help",
|
"help",
|
||||||
|
"helpContextId",
|
||||||
"helperText",
|
"helperText",
|
||||||
"helpText"
|
"helpText"
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -571,6 +571,10 @@ run_manifest_shape_gate() {
|
|||||||
"$META_ROOT/tools/checks/check-manifest-shapes.py" \
|
"$META_ROOT/tools/checks/check-manifest-shapes.py" \
|
||||||
--workspace-root "$PARENT" \
|
--workspace-root "$PARENT" \
|
||||||
--require-architecture
|
--require-architecture
|
||||||
|
run env PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
"$PYTHON" \
|
||||||
|
"$META_ROOT/tools/checks/check-dsar-coverage.py" \
|
||||||
|
--workspace-root "$PARENT"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_migration_release_audit() {
|
run_migration_release_audit() {
|
||||||
|
|||||||
Reference in New Issue
Block a user