Files
govoplan-scheduling/README.md

274 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GovOPlaN Scheduling
<!-- govoplan-repository-type:start -->
**Repository type:** module (domain).
<!-- govoplan-repository-type:end -->
GovOPlaN Scheduling owns meeting scheduling and `Terminfindung`: finding
suitable times across people, resources, calendars, constraints, and scheduling
polls.
This repository is initialized as a discoverable module seed. It exposes the
module manifest, permission surface, role templates, and documentation metadata
before runtime APIs, database models, migrations, and WebUI routes are
introduced.
The core boundary decision register is in
`/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`.
## Scope
Scheduling owns:
- meeting scheduling polls and proposals built on `govoplan-poll`
- participant availability collection
- time-window and resource constraints
- candidate-slot ranking and conflict explanation
- scheduling state, reminders, and decision handoff
- optional links to external calendar/groupware systems through capabilities
- organizer permissions and poll administration semantics
Scheduling does not own:
- calendar primitives such as events, recurrence, availability storage, resources, and calendar adapters; those belong in `govoplan-calendar`
- fixed-slot public or internal appointment booking; that belongs in `govoplan-appointments`
- tasks, case work, approvals, and generic workflow state; those belong in `govoplan-tasks`, `govoplan-cases`, and `govoplan-workflow`
- mail delivery or notifications; those belong in `govoplan-mail` and `govoplan-notifications`
## Poll Audience Decision
Scheduling polls should support both internal and external/public
participation, but the first implementation may start internal-only.
- Internal polls use Access/IDM identities, groups, permissions, and calendar
free/busy capability lookups.
- External/public polls use signed participation links through Portal, minimal
participant identity, and explicit retention/privacy settings.
- Mixed polls are allowed only when the organizer has permission to invite
external participants and the poll explains which participant data is visible.
## Privacy, Retention, And Audit
Participant availability is sensitive operational data. Scheduling must record:
- poll creator, tenant, organizer, invited participants, and access scope
- what each participant can see about other participants
- retention deadline for availability responses and poll links
- audit events for poll creation, invite send, response update, decision,
cancellation, and handoff
- trace IDs for mail/notification/portal handoff
Availability responses should be removable or redacted after the poll decision
unless a configured process requires longer evidence retention.
## Candidate Capabilities
- `scheduling.polls`
- `scheduling.availabilityCollector`
- `scheduling.candidateSlots`
- `scheduling.decisionHandoff`
- `scheduling.portalParticipation`
## Manifest Dependency Decision
The Scheduling manifest declares `poll` as a required dependency because
availability collection and option/date poll primitives belong in
`govoplan-poll`.
Scheduling should model each meeting-finding flow as a poll-backed workflow:
Poll owns candidate options, signed participation links, responses, visibility,
and result aggregation. Scheduling stores the scheduling-specific context and
uses Poll context fields to point back to its request or proposal resource.
Typical workflow steps are collect availability, rank candidates, decide, notify
participants, and hand off to Calendar or Appointments.
The manifest declares `access`, `addresses`, and `evaluation` as optional
dependencies. Scheduling uses Core's principal-aware people-search boundary to
combine only the account and contact records visible to the current organizer;
it never calls the instance-wide Identity search. It may trigger post-event or
post-appointment feedback through Evaluation, and must not require any of these
optional modules just to find a meeting time.
## Expected Integrations
- `govoplan-poll`: required availability matrix and lightweight poll primitives
- `govoplan-evaluation`: optional post-event, post-appointment, or process feedback
- `govoplan-calendar`: availability, calendars, resources, rooms, recurrence, Open-Xchange/calendar adapters
- `govoplan-appointments`: conversion from a selected meeting time into a booked appointment where appropriate
- `govoplan-access` and `govoplan-idm`: optional participants, groups, directory lookup, permissions
- `govoplan-mail` and `govoplan-notifications`: invitations, reminders, confirmations
- `govoplan-portal`: external participant scheduling flows
- `govoplan-workflow` and `govoplan-tasks`: follow-up work after a time is selected
## Participant selection boundary
The editor uses Core's shared `PeoplePicker`. Its server-side search aggregates
the optional `access.people_search` and `addresses.people_search` capabilities,
and each provider applies the active principal and tenant visibility rules
before returning a candidate. Scheduling exposes only the fields needed to
select a person; provider provenance, address-book topology, group membership,
and other internals are not returned by its picker endpoint.
An account selection becomes an internal participant bound to that account.
A visible address-book contact becomes an external participant with a bounded
directory-selection reference and revision for later organizer editing. Manual
name-and-email entry is available only while the request allows external
participants. The picker stores neither provider-internal provenance nor a
global Identity record reference in participant metadata.
## First Package Scaffold Decision
The first backend implementation slice adds runtime APIs and storage around
poll-backed scheduling requests:
- scheduling request, candidate slot, and participant storage
- automatic `govoplan-poll` availability poll creation
- signed poll invitations for internal or external participants
- request lifecycle APIs: draft, collecting, closed, decided, handed off, cancelled
- result summaries sourced from Poll response aggregation
- optional Calendar free/busy checks, tentative holds, and final event creation
- notification outbox jobs for invitations, reminders, decisions,
cancellations, and participant access changes
- a first Scheduling WebUI package with request creation, slot matrix, Calendar
actions, decisions, and notification-job creation
The next slices should add generic self-enrolment links after their abuse and
identity policy is agreed, Calendar hold cleanup after decision, and advanced
scoring constraints such as required participants and quorum rules.
The active backlog lives in Gitea issues.
## Signed-participation policy gate
Scheduling persists the response policy and snapshots it onto each governed
Poll invitation. Public access uses
`/scheduling/public/{request_id}/{token}`; Poll's ordinary public routes reject
these gateway-bound tokens, so callers cannot bypass Scheduling's password and
request checks. Passwords are write-only, stored only as salted PBKDF2 hashes,
and failed checks are throttled through Redis when configured with a bounded
in-process fallback.
Poll is the transactional authority for response rules. Its governed submission
holds the Poll-row lock while enforcing single choice, `Maybe`, participant
email, comments, idempotency and per-option capacity. Scheduling then updates
its participant projection in the same database transaction. Candidate-slot
add/remove and participant-link revocation also go through the governed Poll
capability; exact retries are idempotent, and option mutations invalidate only
the affected answers.
Public submissions lock the Scheduling request and participant rows before
entering Poll, matching organizer edit lock order and making first-use email
binding atomic. Both authenticated and public submission paths independently
require the Scheduling request to be collecting and its deadline not to have
passed, so a stale or incorrectly reopened Poll projection cannot accept a
response. Changing a request deadline transactionally updates each governed
invitation's expiry in Poll under owner- and gateway-bound row locks. The same
link and invitation identity survive future, past, extended, and cleared
deadlines: a past deadline makes the link unusable, a later extension makes the
same link usable again, and clearing the deadline removes its expiry. No raw
replacement token crosses the PATCH response, and existing responses plus
participant status remain attached to the same durable respondent identity.
Cancellation closes the backing Poll, so submission fails, while active links
remain usable as a reduced cancellation notice for a bounded period. The
deployment setting `SCHEDULING_CANCELLATION_NOTICE_DAYS` defaults to 30 and is
bounded to 190 days. Cancellation transactionally aligns governed invitation
expiry with that timestamp. The public projection contains only the request
title and cancellation timestamps; descriptions, locations, candidate slots,
comments, and previous answers are omitted. After the bound, access fails with
the same generic response as an invalid or expired link.
If the governed capability is absent, API responses advertise that policy
enforcement is unavailable and restricted links fail closed. Plaintext
passwords, token hashes, response-gateway internals and the participant roster
are not exposed by the public response model.
Authenticated participant list/detail projections likewise omit tenant,
organizer, Poll and Calendar identifiers, connector metadata, invitation and
identity bindings. Free/busy conflicts are reduced to useful time/status
fields. Organizers and scheduling administrators retain the full management
projection; participants retain candidate-slot revisions, their own marker and
email, response settings, aggregates, and any roster names/statuses permitted
by the configured privacy policy.
The WebUI package registers `/scheduling/public/{request}/{token}` through
Core's explicit, backend-allowlisted public-route contract. The guest page uses
the shared UI components, prompts for email/password only when needed, prefills
an existing response, and enforces the snapshotted response rules. The token
stays in the path and is never copied into query parameters or browser storage.
Signed-in users also receive an in-app deep link without weakening the signed
guest-link boundary.
Creating, editing, and opening a request never issue public tokens or enqueue
invitation delivery. The deprecated `create_participant_invitations` request
field remains accepted for compatibility, defaults to `false`, and has no side
effect. Authenticated in-module responses can still lazily create a
gateway-bound invitation whose token is discarded.
Organizers and Scheduling administrators use the participant-specific
invitation action instead:
- `POST /scheduling/requests/{request_id}/participants/{participant_id}/invitation`
with `{"action":"copy","participant_revision":"..."}` rotates the previous
invitation and returns the new relative action URL once. The response is
marked `no-store`.
- The same endpoint with `{"action":"send"}` rotates the invitation and passes
its URL directly to the notification dispatch job; it also requires the
current `participant_revision`. Neither the API response nor Scheduling's
durable notification projection contains the token.
- `DELETE` on the same resource uses a JSON body containing the current
`participant_revision` and revokes the active link immediately. A revoke
against the refreshed no-link projection is an idempotent replay.
The semantic participant revision includes the current invitation identity.
It is checked after the participant row is locked, so stale copy, send, and
revoke commands return `409` before rotating a newer link or delivering to a
changed recipient.
The participant DataGrid presents copy, send, and revoke as a fixed icon-only
action group. Authorized but unavailable actions remain visible and explain
why they are disabled: for example, delivery is disabled without a dispatch
provider or recipient target, and revoke is disabled when no active link
exists. The delivery capability is exposed only in management projections.
Copy accepts only the same-origin Scheduling public path and does not persist
the bearer URL in component state, logs, or browser storage.
Links can be issued in any request state. Collection state and deadline checks
remain independent submission requirements, so a link to a draft, closed, or
decided request is read-only. Issue, copy, send-request, and revoke actions are
audited with request and participant identifiers but never a bearer token.
Only an organizer (under the ordinary Scheduling write policy) or a tenant-wide
Scheduling administrator can use these actions. If notification delivery is
not installed, `send` fails before rotating the current link and the organizer
can use `copy` for separate distribution.
Participant edits carry a semantic revision so a stale organizer form cannot
overwrite an invitation or response change. Corrections that retain a stable
account or directory identity update the existing participant. A display-name
correction keeps its invitation; changing a delivery email revokes the stale
link but keeps a response tied to the unchanged account identity.
Changing the canonical identity creates a new participant instead of assigning
the former participant's response to another person. In the same transaction,
Scheduling revokes the old invitation and Poll soft-deletes every matching
live response. Poll retains the answers and a bounded retirement record for
audit, while result summaries and capacity checks immediately exclude them.
Removing an invited or responding participant follows the same retirement
path. Scheduling records privacy-safe audit facts and queues a removal or
replacement notice to the former recipient without placing email addresses or
response contents in the audit event.
## FieldLabel omission register
Scheduling uses the shared `FieldLabel` for form controls that need explanatory
inline help. The only intentional omissions are:
- Candidate-slot and participant DataGrid cells: column headers supply the
visible label and each interactive cell has an accessible name.
- Per-slot availability selects: the enclosing visible slot/date text is the
native label for that individual choice.
There are no other authorized Scheduling omissions. Inline help remains
controlled by the user's shared interface setting; hiding it does not remove
accessible labels.