# Policy Decision And Provenance Contract `govoplan-policy` owns policy and retention route contributions. Core keeps the small shared DTOs that let policy decisions look the same across modules. Privacy retention implementation lives in this module at `govoplan_policy.backend.retention`. It is also exposed as the `policy.privacyRetention` capability so older core compatibility imports can dispatch to the active policy module without owning policy logic in core. Core does not import this implementation as a hidden fallback when policy is disabled. Reusable hierarchical policy validation lives in `govoplan_policy.backend.hierarchy`. Policy families should use that helper for parent locks, lower-level override ceilings, "more restrictive only" checks, and read-only simulations before destructive or limiting changes are saved. Domain modules keep their own policy fields and restriction rules, but the decision shape and simulation payload stay consistent. When retention needs audit-log storage behavior, it requests the `audit.retention` capability; it does not import audit module tables or providers directly. ## Scheduling Participant Privacy Policy exposes `policy.schedulingParticipantPrivacy` as an optional restriction hook. Scheduling supplies the request-level visibility choice and remains responsible for its secure fallback when Policy is absent. The provider never broadens that choice. System and tenant settings may contain: ```json { "scheduling_participant_privacy_policy": { "maximum_visibility": "aggregates_only" } } ``` `maximum_visibility` is either `aggregates_only` or `names_and_statuses`. Omitted settings impose no additional restriction. Effective visibility is the most restrictive of the Scheduling request, the system ceiling, and the tenant ceiling. Explicit malformed policy fails closed to `aggregates_only` and is reported in decision details without echoing the invalid stored value. Policy sources include only explicitly configured or invalid system and tenant steps. The current slice is resolver-only. A managed write API and administration UI must add validation, audit, parent-ceiling enforcement, and configuration safety registration before operators can edit this setting through GovOPlaN. ## Backend DTOs Use `govoplan_core.core.policy.PolicyDecision` for explainable policy results: - `allowed`: effective decision for the checked action. - `reason`: compact operator-readable explanation. - `source_path`: ordered policy sources that produced the decision. - `requirements`: machine-readable blockers or prerequisites. - `details`: domain-specific structured context, redacted when needed. Use `PolicySourceStep` or `policy_source_step()` for each provenance step: - `scope_type`: `system`, `tenant`, `user`, `group`, or `campaign`. - `scope_id`: stable ID for non-system scopes. - `path`: stable string path generated by `policy_source_path()`. - `label`: concrete source label such as `System`, `Tenant`, `Owner user`, `Group`, or `Campaign`. - `applied_fields`: field names affected by that step. - `policy`: local policy fragment that explains the applied fields. Do not build or split provenance paths manually. Use `policy_source_path()` and `parse_policy_source_path()` so IDs are URL-encoded consistently. ## Retention Explain Endpoint Retention policy exposes the shared shape through: ```text GET /api/v1/admin/privacy-retention/policies/{scope_type}/explain ``` The response contains `decision`, `effective_policy`, `parent_policy`, `effective_policy_sources`, `parent_policy_sources`, and `blocked_fields`. Retention policy also exposes a write-preflight endpoint: ```text POST /api/v1/admin/privacy-retention/policies/{scope}/simulate ``` The request body is the same as the write endpoint. The response contains a `simulation` object with `allowed`, `changed_fields`, `issues`, `before_policy`, `requested_policy`, and a shared `PolicyDecision` payload. The endpoint never writes policy state and is intended for UI validation before operators attempt destructive or limiting changes. Clients can use `blocked_fields` to disable controls before a save attempt. ## UI Expectations Policy UIs should render provenance close to the effective column or field it explains. The display path should use concrete source labels and local values, for example: ```text System: Allow > Tenant: Deny without override ``` If all lower levels still inherit, continue the path until the effective local decision: ```text System: Allow > Tenant: Inherit > Group: Inherit > Campaign: Deny ``` When a parent disallows lower-level limits or changes, the UI should disable the affected controls and avoid sending those fields in the save payload. The shared core WebUI helper `PolicySourcePath` renders the source path shape for module UIs. Modules may use their own field layout, but the data contract should remain this shape.