Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

154 lines
12 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.

# PRD — Workflow Business-Day Scheduling
- Slug: `workflow-business-day-scheduling`
- Date: `2026-04-10`
- Status: Draft
## Summary
Extend Workflow Runtime V2 recurring schedules so admins can constrain cron-based workflow runs to business days or non-business days using the tenants existing SLA business-hours schedules and holidays. V1 applies only to recurring workflow schedules, skips disallowed occurrences instead of deferring them, defaults to the tenants default business-hours schedule, and allows an optional per-workflow-schedule override.
## Problem
Workflow schedules currently understand only raw time triggers (`schedule` and `recurring`). They cannot express common operational policies like “run this workflow only on business days” or “run this workflow only on non-business days/holidays.” Meanwhile, the product already has a tenant-owned business-hours and holiday system used by SLA calculations. Without reusing that system, customers either over-schedule workflows and tolerate unwanted holiday/weekend runs or create brittle manual workarounds.
## Goals
- Let recurring workflow schedules run on any day, business days only, or non-business days only.
- Reuse the existing tenant business-hours schedules and holidays rather than creating a separate workflow calendar model.
- Default filtered workflow schedules to the tenants default business-hours schedule, while allowing an explicit schedule override per workflow schedule.
- Treat holidays as non-business days for workflow day filtering, including when the selected business-hours schedule is marked 24x7.
- Skip disallowed cron occurrences without launching workflow runs or creating catch-up/deferred executions.
- Validate schedule configuration strictly at save time so admins cannot persist a business/non-business filter without an effective business-hours schedule.
- Surface enough schedule state in API/UI so admins can understand why a filtered recurring schedule will or will not run.
## Non-goals
- Adding business/non-business-day filtering to one-time (`schedule`) workflow triggers.
- Replacing pg-boss recurring registration with a custom “next eligible occurrence only” scheduler.
- Creating a new workflow-specific calendar, holiday, or availability subsystem.
- Adding user-selectable “defer to next allowed day” semantics in v1.
- Introducing company-calendar or external calendar integrations as part of this work.
- Adding observability/analytics/feature-flag rollout work unless requested separately.
## Users and Primary Flows
### Primary user
- Workflow admin configuring recurring automations in the Automation Hub / workflow designer.
### Primary flows
1. Create or edit a recurring workflow schedule, choose `Business days only`, leave calendar source at `Tenant default business hours`, and save successfully because the tenant has a default business-hours schedule.
2. Create or edit a recurring workflow schedule, choose `Non-business days only`, select a specific business-hours schedule override, and save successfully.
3. Attempt to save a filtered recurring workflow schedule when neither a specific override nor a tenant default business-hours schedule exists, and receive a validation error that explains the missing calendar configuration.
4. Allow cron to fire normally, but skip workflow launch on disallowed dates such as holidays or weekdays/weekends outside the chosen business-day classification.
5. Review the schedule later in the UI/API and see the configured day filter, chosen calendar source, and latest skip/error state.
## UX / UI Notes
- In the recurring schedule editor, add a `Run on` control with three options:
- `Any day`
- `Business days only`
- `Non-business days only`
- Show the day-filter controls only for recurring schedules.
- When `Business days only` or `Non-business days only` is selected, show calendar-source controls:
- `Tenant default business hours`
- `Specific business-hours schedule`
- When `Specific business-hours schedule` is selected, show a select populated from the tenants available business-hours schedules.
- Provide inline help text clarifying that holidays are treated as non-business days.
- If the API returns an effective next eligible run for a filtered recurring schedule, display it in schedule details/listing in preference to the raw cron tick when practical.
## Requirements
### Functional Requirements
- `FR-001` Add recurring workflow schedule day-filter support with allowed values `any`, `business`, and `non_business`.
- `FR-002` Persist day-filter configuration as scheduler metadata on `tenant_workflow_schedule`, not inside workflow payload JSON.
- `FR-003` Persist an optional `business_hours_schedule_id` override on `tenant_workflow_schedule`; `null` means use the tenant default business-hours schedule.
- `FR-004` Restrict business/non-business-day filtering to recurring workflow schedules; one-time schedules must reject non-`any` day filters.
- `FR-005` Validate on create/update that a recurring schedule using `business` or `non_business` filtering resolves to an effective business-hours schedule via explicit override or tenant default.
- `FR-006` Validate that any selected `business_hours_schedule_id` belongs to the current tenant.
- `FR-007` Resolve filtered recurring schedules against the selected business-hours schedule plus both global holidays (`schedule_id IS NULL`) and schedule-specific holidays for that schedule.
- `FR-008` Classify holidays as non-business days for workflow filtering even when the chosen business-hours schedule is marked `is_24x7 = true`.
- `FR-009` Classify non-holiday dates as business days only when the chosen business-hours schedule has an enabled entry for the local weekday; otherwise classify them as non-business days.
- `FR-010` Evaluate business/non-business-day eligibility using the scheduled occurrences local date in the schedule timezone, not the workers wall-clock execution time.
- `FR-011` Preserve existing cron/pg-boss recurring registration behavior; the new filter narrows execution eligibility rather than replacing recurrence scheduling.
- `FR-012` When a recurring cron tick lands on a disallowed day, skip the occurrence without launching a workflow run and without creating a deferred/catch-up execution.
- `FR-013` Record skipped filtered occurrences distinctly from workflow-run failures so operators can tell the difference between policy skips and execution errors.
- `FR-014` When a filtered recurring schedule becomes invalid at runtime because its selected/default business-hours schedule can no longer be resolved, fail fast, mark the schedule with an actionable error state, and do not launch the workflow.
- `FR-015` Extend schedule list/get/create/update API contracts to include day-filter and business-hours schedule override fields.
- `FR-016` Load available business-hours schedules into the recurring schedule dialog so users can choose a specific override.
- `FR-017` In the recurring schedule dialog, show day-filter controls and calendar-source controls only when relevant to the current trigger type and filter selection.
- `FR-018` Surface save-time validation errors in the UI when no effective business-hours schedule exists or the selected override is invalid.
- `FR-019` Preserve legacy schedules that do not use day filtering; existing recurring schedules should behave exactly as before with default `day_type_filter = any`.
- `FR-020` Surface the configured day filter and calendar selection in schedule list/detail responses so the UI can render and edit them accurately.
- `FR-021` Compute and expose an effective next eligible run for filtered recurring schedules in list/detail responses when that value can be derived within a bounded search window.
- `FR-022` Return `null`/no effective next eligible run when no eligible occurrence can be found within the bounded preview window.
### Non-functional Requirements
- `NFR-001` The implementation must reuse the existing tenant business-hours schedules and holidays model rather than introducing a duplicate calendar schema.
- `NFR-002` Existing recurring and one-time workflow schedules without day filters must remain backward-compatible across migration, listing, editing, and runtime execution.
- `NFR-003` Runtime failure handling must be explicit and actionable; no silent fallback to 24x7 semantics when a filtered schedule has no effective business-hours schedule.
- `NFR-004` The search for an effective next eligible run must be bounded (for example by occurrence count and/or time horizon) to avoid unbounded compute on impossible schedules.
- `NFR-005` The business-day classification logic used by validation, preview, and runtime skipping must be centralized so behavior stays consistent across action/UI/handler paths.
## Data / API / Integrations
- Add scheduler metadata columns to `tenant_workflow_schedule`:
- `day_type_filter` text not null default `'any'`
- `business_hours_schedule_id` uuid null
- Extend `WorkflowScheduleStateRecord` and associated persistence helpers to read/write those fields.
- Extend workflow schedule v2 action schemas and responses to accept/return those fields.
- Reuse tenant business-hours schedule data from:
- `business_hours_schedules`
- `business_hours_entries`
- `holidays`
- Use the same holiday-resolution model already used by SLA services:
- global holidays (`schedule_id IS NULL`)
- plus schedule-specific holidays for the selected schedule.
- Introduce a shared workflow-side helper/service to:
- resolve the effective business-hours schedule
- classify a scheduled date as business/non-business
- optionally search ahead for the next eligible cron occurrence for UI/API preview
## Security / Permissions
- No new permission model is introduced.
- Existing workflow read/manage permissions continue to govern schedule listing and mutation.
- Business-hours schedule overrides must be tenant-scoped and validated against the current tenant.
## Observability
- No new observability work is in scope for v1 beyond storing actionable schedule status/error information needed by operators in existing schedule views.
## Rollout / Migration
- Add the new columns with safe defaults so all existing rows become `day_type_filter = 'any'` and continue operating unchanged.
- No backfill beyond defaults is required.
- UI should treat absent/legacy values as `any` when editing old schedules.
- Runtime should preserve current behavior for all schedules without a non-`any` day filter.
## Risks
- There are now two useful “next run” concepts for filtered recurring schedules: raw cron tick and effective next eligible run. The API/UI must be clear which one is being shown.
- Some cron/day-filter combinations may produce very sparse eligible dates (for example weekday-only cron plus non-business-only filtering), so bounded search behavior must be deterministic.
- Runtime and action validation must agree on business-day classification or operators will see save/execute mismatches.
- Selected/default business-hours schedules can drift after save; runtime misconfiguration handling must be explicit and understandable.
## Open Questions
- None for v1 based on current design approval. Future work may consider whether to expose raw cron next tick alongside effective next eligible run in the UI.
## Acceptance Criteria (Definition of Done)
- An admin can configure a recurring workflow schedule to run on any day, business days only, or non-business days only.
- A filtered recurring schedule can use either the tenants default business-hours schedule or a specific business-hours schedule override.
- Saving a filtered recurring schedule fails with a clear validation message if no effective business-hours schedule exists.
- Cron-triggered recurring schedules on disallowed dates are skipped rather than executed or deferred.
- Holidays are treated as non-business days for workflow scheduling, including when the chosen business-hours schedule is 24x7.
- Existing schedules without day filters remain unaffected after migration and continue to execute as before.
- Schedule list/detail/edit flows preserve and display the new filter/calendar fields accurately.
- Filtered recurring schedules surface actionable skip/error state and, when derivable within bounds, an effective next eligible run.