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

22 KiB

Scratchpad — Workflow V2 Time-Based Clock Triggers

  • Plan slug: workflow-v2-time-based-clock-triggers
  • Created: 2026-03-07

What This Is

Keep a lightweight, continuously-updated log of discoveries and decisions made while planning and implementing Workflow V2 pure clock triggers.

Decisions

  • (2026-03-07) Scope starts with pure clock triggers only. Each fire starts one workflow run. No domain-record fan-out.
  • (2026-03-07) Cron support is limited to 5-field cron. No seconds field and no 6-field cron syntax.
  • (2026-03-07) This capability is EE-only.
  • (2026-03-07) “No trigger” remains the no-trigger shape. Do not add an explicit manual trigger type in this scope.
  • (2026-03-07) Preferred architecture is first-class time triggers, not synthetic event catalog entries.
  • (2026-03-07) Preferred scheduling substrate is the job-runner abstraction (scheduleJobAt and scheduleRecurringJob), not the legacy JobScheduler.
  • (2026-03-07) Time-triggered workflows should use a fixed synthetic payload contract and pinned payload schema mode in v1.
  • (2026-03-07) Run creation should be unified behind one launcher service shared by event and time triggers.
  • (2026-03-07) Canonical Workflow V2 trigger discriminants will be event, schedule, and recurring; one-time and recurring triggers stay first-class instead of overloading event names.
  • (2026-03-07) Create/update action inputs should continue to accept trigger variants by delegating to workflowDefinitionSchema rather than duplicating a second trigger union.

Discoveries / Constraints

  • (2026-03-07) The canonical Workflow V2 definition schema only supports trigger.type = 'event' today. Key file: shared/workflow/runtime/types.ts.
  • (2026-03-07) The designer trigger UX is event-only and tightly coupled to event catalog lookups, source schema overrides, and trigger mapping. Key file: ee/server/src/components/workflow-designer/WorkflowDesigner.tsx.
  • (2026-03-07) Publish validation is split between shared runtime validation and server-action validation. Trigger-specific logic currently assumes event triggers.
  • (2026-03-07) Event-trigger run start logic already exists in more than one path and has behavioral drift. Adding a third bespoke path for time triggers would worsen consistency problems.
  • (2026-03-07) Workflow V2 already has durable wait/retry timing inside runs, but not durable scheduled starts for workflow definitions.
  • (2026-03-07) The extension scheduler is the strongest reusable scheduling implementation already in the repo and persists both internal job ids and external runner schedule ids.
  • (2026-03-07) IJobRunner supports both one-time scheduled execution and recurring scheduled execution. This is the correct reuse seam for workflow time triggers.
  • (2026-03-07) The legacy JobScheduler is not a safe base for real cron semantics because it coarsens cron-ish input to a delayed interval path.
  • (2026-03-07) Current workflow list/filter code still contains trigger-type heuristics that infer “scheduled” from event-name strings. That should be removed once real trigger types exist.
  • (2026-03-07) Existing job-runner handler signatures do not provide a per-occurrence recurring fire id. PgBoss cron schedules inject the stable registration jobServiceId into each delivery, and the runner calls handlers with that value instead of the transient PgBoss job id. Temporal recurring schedules likewise start each occurrence with the stable registration jobRecord.jobId. That means scheduleId + jobId is safe for one-time schedules but would incorrectly dedupe every recurring fire after the first one. Key files: server/src/lib/jobs/runners/PgBossJobRunner.ts, ee/server/src/lib/jobs/runners/TemporalJobRunner.ts, ee/temporal-workflows/src/workflows/generic-job-workflow.ts.
  • (2026-03-07) Importing the broad server/src/lib/jobs barrel from workflow runtime code drags in unrelated handlers; for scheduling lifecycle code, the safer seam is server/src/lib/jobs/JobRunnerFactory so action/import paths do not depend on optional job-handler modules.
  • (2026-03-07) Existing workflow_runs columns are sufficient for event provenance but not honest enough for time-trigger provenance. Added explicit trigger_type plus trigger_metadata_json so clock-triggered runs are not represented as fake event names.
  • (2026-03-07) Direct trigger.eventName and trigger.sourcePayloadSchemaRef access already exists in shared bundling, runtime actions, and run-studio UI; widening the trigger union requires explicit event-trigger narrowing at those call sites.
  • (2026-03-07) payload_schema_mode is persisted on the workflow definition record, not on publish input. That means publish must explicitly reject time-trigger definitions if an older draft is still marked inferred.
  • (2026-03-07) Local Workflow V2 integration tests in this worktree could not use the usual DB-backed harness because PostgreSQL was unavailable on localhost:5438; a mock-backed unit suite was a better fit for publish-path validation coverage.
  • (2026-03-07) The paged workflow list action still filtered scheduled workflows by checking trigger.eventName for schedule/cron substrings. Real trigger filtering needs to key off trigger.type.
  • (2026-03-08) Workflow V2 server actions resolve auth/database through @alga-psa/auth and @alga-psa/db, not the older server/src/lib/db seam used by some legacy tests. The new DB-backed integration coverage had to mock those actual entrypoints to keep action writes on the test database.
  • (2026-03-08) The EE schedule-state table stores job_id as a UUID, so DB-backed scheduling tests must return UUID-shaped mocked runner job ids even when the runner itself is stubbed.

Commands / Runbooks

  • (2026-03-07) Inspect Workflow V2 trigger contract:
    • sed -n '260,325p' shared/workflow/runtime/types.ts
  • (2026-03-07) Inspect Workflow Designer trigger UI:
    • rg -n "workflow-designer-trigger|Trigger event|payloadMapping|event catalog" ee/server/src/components/workflow-designer/WorkflowDesigner.tsx
    • sed -n '3460,3875p' ee/server/src/components/workflow-designer/WorkflowDesigner.tsx
  • (2026-03-07) Inspect publish validation and event-trigger launch path:
    • rg -n "computeValidation|submitWorkflowEventAction|publishWorkflowDefinitionAction" packages/workflows/src/actions/workflow-runtime-v2-actions.ts
  • (2026-03-07) Inspect scheduling substrate:
    • sed -n '36,140p' server/src/lib/jobs/interfaces/IJobRunner.ts
    • sed -n '400,590p' ee/server/src/lib/extensions/schedulerHostApi.ts
    • sed -n '260,420p' server/src/lib/jobs/runners/PgBossJobRunner.ts
  • (2026-03-07) Validate initial schema slice:
    • cd server && pnpm vitest run src/test/unit/workflowTimeTriggerSchemas.unit.test.ts --config vitest.config.ts
    • pnpm exec eslint shared/workflow/runtime/types.ts shared/workflow/bundle/dependencySummaryV1.ts packages/workflows/src/actions/workflow-runtime-v2-actions.ts ee/server/src/components/workflow-run-studio/RunStudioShell.tsx server/src/test/unit/workflowTimeTriggerSchemas.unit.test.ts
  • (2026-03-07) Validate time-trigger publish/contract slice:
    • cd server && pnpm vitest run src/test/unit/workflowTimeTriggerSchemas.unit.test.ts src/test/unit/workflowTimeTriggerPublishValidation.unit.test.ts --config vitest.config.ts
    • pnpm exec eslint shared/workflow/runtime/types.ts shared/workflow/runtime/init.ts shared/workflow/runtime/index.ts shared/workflow/runtime/schemas/workflowClockTriggerSchema.ts server/src/lib/features.ts packages/workflows/src/actions/workflow-runtime-v2-actions.ts server/src/test/unit/workflowTimeTriggerPublishValidation.unit.test.ts
  • (2026-03-07) Validate workflow list trigger-type filtering:
    • cd server && pnpm vitest run src/test/unit/workflowDefinitionListTriggerFilters.unit.test.ts --config vitest.config.ts
    • pnpm exec eslint packages/workflows/src/actions/workflow-runtime-v2-schemas.ts packages/workflows/src/actions/workflow-runtime-v2-actions.ts packages/workflows/src/components/automation-hub/WorkflowList.tsx server/src/test/unit/workflowDefinitionListTriggerFilters.unit.test.ts
  • (2026-03-07) Validate Workflow Designer time-trigger UI:
    • pnpm exec eslint ee/server/src/components/workflow-designer/WorkflowDesigner.tsx ee/server/src/__tests__/page-objects/WorkflowDesignerPage.ts ee/server/src/__tests__/integration/workflow-designer-time-triggers.playwright.test.ts
    • cd ee/server && npm run typecheck
    • cd ee/server && npx playwright test -c playwright.config.ts src/__tests__/integration/workflow-designer-time-triggers.playwright.test.ts --headed
  • (2026-03-07) Validate scheduling lifecycle and launcher wiring:
    • cd server && pnpm vitest run src/test/unit/workflowTimeTriggerSchedulingLifecycle.unit.test.ts src/test/unit/workflowScheduledRunHandlers.unit.test.ts src/test/unit/workflowEventLauncherRouting.unit.test.ts --config vitest.config.ts
    • cd ee/server && npm run typecheck
  • (2026-03-07) Attempt event-trigger regression validation after launcher extraction:
    • cd server && pnpm vitest run src/test/integration/workflowRuntimeV2.eventTrigger.integration.test.ts --coverage.enabled=false --config vitest.config.ts
  • (2026-03-07) Validate run provenance persistence:
    • cd server && pnpm vitest run src/test/unit/workflowRunTriggerProvenance.unit.test.ts src/test/unit/workflowScheduledRunHandlers.unit.test.ts src/test/unit/workflowEventLauncherRouting.unit.test.ts src/test/unit/workflowTimeTriggerSchedulingLifecycle.unit.test.ts --coverage.enabled=false --config vitest.config.ts
    • cd ee/server && npm run typecheck
  • (2026-03-07) Validate run provenance, dedupe, and UI trigger-label helpers after recurring-fire-key fix:
    • cd server && pnpm vitest run src/test/unit/workflowScheduledRunHandlers.unit.test.ts src/test/unit/workflowRunLauncher.unit.test.ts src/test/unit/workflowRunTriggerProvenance.unit.test.ts src/test/unit/workflowEventLauncherRouting.unit.test.ts src/test/unit/workflowTimeTriggerSchedulingLifecycle.unit.test.ts --coverage.enabled=false --config vitest.config.ts
    • cd ee/server && pnpm vitest run src/__tests__/unit/workflowRunTriggerPresentation.unit.test.ts
    • cd ee/server && npm run typecheck
    • cd ee/temporal-workflows && npm run type-check
  • (2026-03-08) Validate DB-backed schedule registration and publish/event regressions against the local GreenMail PostgreSQL instance:
    • cd server && DB_HOST=localhost DB_PORT=55433 DB_PASSWORD_ADMIN=postpass123 DB_PASSWORD_SERVER=postpass123 DB_USER_ADMIN=postgres DB_USER_SERVER=app_user pnpm vitest run src/test/integration/workflowTimeTriggerScheduling.integration.test.ts --coverage.enabled=false --config vitest.config.ts
    • cd server && DB_HOST=localhost DB_PORT=55433 DB_PASSWORD_ADMIN=postpass123 DB_PASSWORD_SERVER=postpass123 DB_USER_ADMIN=postgres DB_USER_SERVER=app_user pnpm vitest run src/test/integration/workflowTimeTriggerRegression.integration.test.ts --coverage.enabled=false --config vitest.config.ts

Progress Log

  • (2026-03-07) Completed F001/F002 and T001-T005.
    • Added workflowTriggerSchema as a discriminated union in shared/workflow/runtime/types.ts with event, schedule, and recurring variants plus reusable type guards.
    • Kept no-trigger behavior unchanged by leaving trigger optional on workflowDefinitionSchema.
    • Confirmed create/update action inputs accept time-trigger variants via the shared definition schema instead of separate action-only trigger parsing.
    • Added server/src/test/unit/workflowTimeTriggerSchemas.unit.test.ts covering shared schema acceptance, no-trigger preservation, and create/update input parsing.
    • Narrowed existing event-only call sites in dependency summary extraction, workflow runtime actions, and run-studio trigger display so the widened trigger union stays type-safe.
  • (2026-03-07) Completed F003-F007 and T006-T015.
    • Added the fixed clock payload contract schema/registry entry at payload.WorkflowClockTrigger.v1.
    • Time-trigger draft saves now normalize onto the fixed clock payload contract and reject CE create/update attempts at action time.
    • Publish now rejects CE time-trigger workflows and rejects any time-trigger publish attempt that still uses inferred payload schema mode.
    • Added publish-time validation for one-time schedules (runAt required, valid ISO timestamp, future-only) and recurring schedules (valid 5-field cron, valid IANA timezone).
    • Added server/src/test/unit/workflowTimeTriggerPublishValidation.unit.test.ts to cover EE gating, fixed schema resolution, inferred-mode rejection, and one-time/recurring validation behavior without depending on a local PostgreSQL harness.
  • (2026-03-07) Completed F008 and T016-T017.
    • Replaced workflow-definition list filtering on trigger.eventName substring heuristics with direct trigger.type comparisons.
    • Preserved backward compatibility for legacy trigger=scheduled URLs by mapping that filter to both schedule and recurring trigger types.
    • Updated the workflow list UI to label and iconize event, schedule, recurring, and manual directly from persisted trigger types.
    • Added server/src/test/unit/workflowDefinitionListTriggerFilters.unit.test.ts to verify one-time and recurring filters hit the real trigger.type query path and return the corresponding trigger types intact.
  • (2026-03-07) Completed F009-F013 and T018-T021.
    • Replaced the event-only trigger control in WorkflowDesigner.tsx with an explicit trigger-type selector that switches among no trigger, event, one-time schedule, and recurring schedule.
    • Added one-time schedule editing via a future datetime-local input and recurring schedule editing via a cron input plus searchable timezone selector with 5-field guidance.
    • Time-trigger selection now hides event-only catalog/mapping controls, forces the fixed clock contract into pinned mode, and disables schema overrides while a time trigger is active.
    • Added an inline clock-contract preview card that surfaces payload.WorkflowClockTrigger.v1 and its fixed payload fields directly in the trigger section.
    • Added ee/server/src/__tests__/integration/workflow-designer-time-triggers.playwright.test.ts plus page-object helpers for the new controls.
    • npm run typecheck passed in ee/server.
    • The targeted Playwright run could not complete in this environment because the EE Playwright web-server bootstrap never brought http://localhost:3300 up; the worker stalled before any browser steps ran.
  • (2026-03-07) Completed F014-F023 and T008/T025-T034.
    • Added the EE migration ee/server/migrations/20260307200000_create_workflow_schedule_tables.cjs plus shared/workflow/persistence/workflowScheduleStateModel.ts for durable workflow schedule registration state.
    • Added server/src/lib/workflow-runtime-v2/workflowScheduleLifecycle.ts to build desired schedule state, register one-time and recurring jobs through IJobRunner, compensate failed reschedules, and clean up registrations on pause/delete/trigger changes.
    • Added server/src/lib/workflow-runtime-v2/workflowRunLauncher.ts as the shared published-workflow launcher, then routed both submitWorkflowEventAction and WorkflowRuntimeV2EventStreamWorker through it.
    • Added server/src/lib/jobs/handlers/workflowScheduledRunHandlers.ts and registered EE-only one-time/recurring schedule handlers in server/src/lib/jobs/registerAllHandlers.ts.
    • Publishing, pausing, deleting, and re-publishing time-triggered workflows now synchronize schedule registration state from packages/workflows/src/actions/workflow-runtime-v2-actions.ts.
    • Narrowed workflow schedule lifecycle imports to server/src/lib/jobs/JobRunnerFactory so the new scheduler path does not pull the entire jobs barrel during action imports.
    • Added unit coverage in server/src/test/unit/workflowTimeTriggerSchedulingLifecycle.unit.test.ts, server/src/test/unit/workflowScheduledRunHandlers.unit.test.ts, and server/src/test/unit/workflowEventLauncherRouting.unit.test.ts for launcher wiring, fixed payload contract emission, schedule registration calls, pause/delete/change-away cleanup, and one-time/recurring reschedule replacement.
    • Left T022-T024 false because this checkpoint still lacks DB-backed integration coverage that exercises real tenant_workflow_schedule rows against a migrated test database; current coverage is action/handler-focused unit coverage.
    • Left T046 false because the targeted event-trigger integration suite could not run in this local environment: PostgreSQL was unavailable on 127.0.0.1:5438/::1:5438, so the suite skipped its tests during setup and failed before exercising the regression path.
  • (2026-03-07) Completed F024 and T035/T037/T038.
    • One-time schedule handler success now marks tenant_workflow_schedule rows completed, clears runner handles, disables the schedule, and nulls next_fire_at.
    • Re-delivery after a successful one-time fire now short-circuits on the existing enabled/status guard, so later ticks do not launch another run.
    • Re-ran cd server && pnpm vitest run src/test/unit/workflowScheduledRunHandlers.unit.test.ts src/test/unit/workflowEventLauncherRouting.unit.test.ts src/test/unit/workflowTimeTriggerSchedulingLifecycle.unit.test.ts --coverage.enabled=false --config vitest.config.ts.
  • (2026-03-07) Completed F025 and T039/T040.
    • Added server/migrations/20260308010000_add_workflow_run_trigger_provenance.cjs to store trigger_type and trigger_metadata_json on workflow_runs.
    • WorkflowRuntimeV2.startRun() now persists trigger provenance explicitly, and launchPublishedWorkflowRun() threads that metadata through for both event and time-triggered launches.
    • Time-trigger job handlers now pass the fixed clock payload through as trigger metadata, while event launches tag runs as trigger_type = 'event' with lightweight trigger metadata for parity.
    • listWorkflowRunsAction() now selects the new provenance fields so downstream UI can distinguish schedule and recurring runs without inferring from event_type.
    • Added server/src/test/unit/workflowRunTriggerProvenance.unit.test.ts and re-ran the unit provenance/lifecycle suite plus cd ee/server && npm run typecheck.
  • (2026-03-07) Completed F026/F027 and T036/T041-T044.
    • Added shared trigger/schedule presentation helpers at ee/server/src/components/workflow-designer/workflowRunTriggerPresentation.ts and wired workflow run list/details plus Run Studio to show one-time/recurring labels and current schedule state from getWorkflowScheduleStateAction().
    • Added server/migrations/20260308013000_add_workflow_run_trigger_fire_key.cjs plus launcher-level duplicate handling keyed by workflow_runs.trigger_fire_key.
    • Corrected the recurring dedupe design after discovering runner jobId is the stable registration id, not a per-occurrence id. PgBoss now injects jobExecutionId = job.id and Temporal now injects jobExecutionId = workflowInfo().workflowId; schedule fire keys now use that runner-supplied execution id so later recurring occurrences still launch while retries of the same occurrence remain idempotent.
    • One-time schedule handlers now store last_fire_key and complete/disable the schedule after a successful fire; recurring handlers store last_fire_key per occurrence while leaving the schedule active.
    • Added/updated unit coverage in server/src/test/unit/workflowScheduledRunHandlers.unit.test.ts, server/src/test/unit/workflowRunLauncher.unit.test.ts, and ee/server/src/__tests__/unit/workflowRunTriggerPresentation.unit.test.ts.
  • (2026-03-08) Completed T022-T024 and T045-T046.
    • Added ensureWorkflowScheduleStateTable() to server/src/test/helpers/workflowRuntimeV2TestUtils.ts and made table resets include tenant_workflow_schedule when present so DB-backed Workflow V2 suites can exercise the EE schedule lifecycle safely.
    • Added server/src/test/integration/workflowTimeTriggerScheduling.integration.test.ts covering one-time schedule row creation, recurring schedule row creation, and persisted runner identifiers through the real publish action with a real PostgreSQL test database and mocked job runner.
    • Added server/src/test/integration/workflowTimeTriggerRegression.integration.test.ts covering no-trigger publish/start regression and event-trigger publish/start regression through the shared launcher path with persisted run provenance assertions.
    • Verified the new DB-backed coverage against the local greenmail_imap_postgres container exposed on localhost:55433.
  • Workflow definition schema: shared/workflow/runtime/types.ts
  • Workflow publish/run actions: packages/workflows/src/actions/workflow-runtime-v2-actions.ts
  • Workflow action input schemas: packages/workflows/src/actions/workflow-runtime-v2-schemas.ts
  • Workflow definition persistence: shared/workflow/persistence/workflowDefinitionModelV2.ts
  • Workflow run worker: shared/workflow/workers/WorkflowRuntimeV2Worker.ts
  • Workflow event-stream worker: services/workflow-worker/src/v2/WorkflowRuntimeV2EventStreamWorker.ts
  • Workflow designer: ee/server/src/components/workflow-designer/WorkflowDesigner.tsx
  • Job runner interface: server/src/lib/jobs/interfaces/IJobRunner.ts
  • PG Boss runner: server/src/lib/jobs/runners/PgBossJobRunner.ts
  • EE Temporal runner: ee/server/src/lib/jobs/runners/TemporalJobRunner.ts
  • EE extension scheduler API: ee/server/src/lib/extensions/schedulerHostApi.ts
  • Extension schedule handler: server/src/lib/jobs/handlers/extensionScheduledInvocationHandler.ts
  • Related existing schedule table migration: ee/server/migrations/20260101120000_create_extension_schedule_tables.cjs
  • New DB-backed schedule tests: server/src/test/integration/workflowTimeTriggerScheduling.integration.test.ts
  • New launcher regression tests: server/src/test/integration/workflowTimeTriggerRegression.integration.test.ts

Open Questions

  • Should completed one-time schedules remain as durable rows forever or be archived/cleaned up later?
  • Should the first version expose next-fire previews in the designer, or leave that to a follow-up?
  • Should workflow run provenance for time triggers use new explicit columns, or be represented through structured run metadata layered on top of current provenance fields?
  • (2026-03-07) What stable per-occurrence key should recurring schedule fires use across retries and across both PgBoss and Temporal runners? Current runner inputs expose schedule registration ids but not an occurrence id.