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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
17 KiB
17 KiB
SCRATCHPAD: Workflow Scheduling Actions
2026-04-25 — Plan Created
Worktree / Branch
- Worktree:
/Users/roberisaacs/alga-psa.worktrees/feature/workflows-scheduling-actions - Current git status before plan edits:
package-lock.jsonalready modified. This plan did not intentionally touch it. - Plan folder:
ee/docs/plans/2026-04-25-workflow-scheduling-actions/
User Request Summary
Add richer workflow scheduling actions so workflow authors can express dispatcher language directly:
scheduling.reschedulescheduling.reassignscheduling.cancelscheduling.complete
Also keep/add the read side:
scheduling.find_entryscheduling.search_entries
Target lifecycle event alignment:
APPOINTMENT_CREATEDAPPOINTMENT_RESCHEDULEDAPPOINTMENT_ASSIGNEDAPPOINTMENT_CANCELEDAPPOINTMENT_COMPLETEDAPPOINTMENT_NO_SHOW
This pass plans the four requested lifecycle write actions plus read actions. NO_SHOW is not included unless scope changes.
Source Investigation Notes
Workflow action architecture
- Action file to change:
shared/workflow/runtime/actions/businessOperations/scheduling.ts - Registration is already wired through
registerSchedulingActions(). - Business operation registration flows through
shared/workflow/runtime/actions/registerBusinessOperationsActions.tsandshared/workflow/runtime/init.ts. - Designer grouping is prefix-based.
scheduling.*maps to the built-in Scheduling group inshared/workflow/runtime/designer/actionCatalog.ts.
Current scheduling workflow action state
shared/workflow/runtime/actions/businessOperations/scheduling.tscurrently only registersscheduling.assign_user.scheduling.assign_user:- Validates user exists.
- Requires the assigned user to have a role named
Technician. - Validates linked ticket/project task exists.
- Handles conflict modes
fail,shift,override. - Inserts
schedule_entriesandschedule_entry_assigneesdirectly. - Inserts unresolved
schedule_conflictsrows on override. - Writes workflow run audit via
writeRunAudit(). - Does not currently publish
APPOINTMENT_CREATEDorAPPOINTMENT_ASSIGNEDworkflow events.
Shared workflow helper state
shared/workflow/runtime/actions/businessOperations/shared.tscontains useful helpers:withTenantTransaction()requirePermission()writeRunAudit()throwActionError()uuidSchemaisoDateTimeSchema
withTenantTransaction()resolves the workflow actor user from workflow run/definition metadata and sets tenant RLS withset_config('app.current_tenant', ...).
Existing scheduling model/actions
packages/scheduling/src/models/scheduleEntry.tsis the tenant-explicit model for schedule entries.- Supports
create(),update(),delete(),get(),getAll(), recurrence handling, and assignee helper methods. update()supports recurrence scopes usingIEditScope:single,future,all.- Virtual recurring ids use an underscore pattern:
<masterEntryId>_<timestamp>. - File currently has
// @ts-nocheck; implementation should be covered by DB-backed tests if imported/used.
- Supports
packages/scheduling/src/actions/scheduleActions.tsis the UI/server action layer.- Uses
withAuth,hasPermission,createTenantKnex, andwithTransaction. - Publishes legacy event-bus events:
SCHEDULE_ENTRY_CREATED,SCHEDULE_ENTRY_UPDATED,SCHEDULE_ENTRY_DELETED. - Publishes workflow domain events using
publishWorkflowEvent:APPOINTMENT_CREATEDAPPOINTMENT_RESCHEDULEDAPPOINTMENT_ASSIGNEDAPPOINTMENT_CANCELEDAPPOINTMENT_COMPLETEDAPPOINTMENT_NO_SHOW- schedule block and technician dispatch events.
- Because of
withAuth, these server actions are not a clean direct dependency for workflow runtime action handlers.
- Uses
Existing event builders/schemas
- Appointment event builders:
shared/workflow/streams/domainEventBuilders/appointmentEventBuilders.tsshouldEmitAppointmentEvents(entry)returns true forwork_item_type === 'appointment_request' || 'ticket'.getTicketIdFromScheduleEntry(entry)maps ticket-linked entries.- Status helpers accept tolerant canceled/completed/no-show spellings.
- Builders include
buildAppointmentRescheduledPayload,buildAppointmentAssignedPayload,buildAppointmentCanceledPayload,buildAppointmentCompletedPayload.
- Workflow runtime event payload schemas:
shared/workflow/runtime/schemas/schedulingEventSchemas.ts - Event package schemas:
packages/event-schemas/src/schemas/domain/schedulingEventSchemas.ts - Existing
APPOINTMENT_ASSIGNEDschema is single-assignee shaped:previousAssigneeId?,previousAssigneeType?,newAssigneeId,newAssigneeType.
Schedule persistence tables
- Initial
schedule_entriestable inserver/migrations/202409071803_initial_schema.cjs. schedule_entry_assigneescreated inserver/migrations/20241227233407_create_schedule_entry_assignees.cjs.user_idremoved fromschedule_entriesinserver/migrations/20241228003050_remove_user_id_from_schedule_entries.cjs.- Recurrence columns added in
server/migrations/20241227234500_add_original_entry_id_to_schedule_entries.cjs:original_entry_idis_recurring
- Ad hoc schedule entries made possible by
server/migrations/20250103172553_add_adhoc_schedule_entries.cjs. is_privateadded inserver/migrations/20250515104157_add_private_flag_to_schedule_entries.cjs.interactionwork item type added inserver/migrations/20250602153500_add_interaction_work_item_type.cjs.appointment_requestwork item type added inserver/migrations/20251111190000_add_appointment_request_work_item_type.cjs.
Permissions
- Current
scheduling.assign_userrequiresuser_schedule:create. - Existing role/permission migrations define
user_schedule:create,read,update,delete. - UI/server scheduling actions generally use:
user_schedule:readfor reads.user_schedule:updatefor broad edits/assignments.- Delete action has custom validation but should map to product decision for workflow cancel semantics.
Approach Options Considered
- Extend
scheduling.tswith local helpers — recommended for this pass.- Smallest change in workflow action layer.
- Avoids
withAuthserver action coupling. - Some duplication with Scheduling server action event logic.
- Extract scheduling domain service — best long-term architecture.
- More reusable.
- Larger refactor and not needed to create first safe workflow action surface.
- Call existing Scheduling server actions — not recommended.
- They are session/auth-bound and not designed for workflow runtime's explicit tenant/run actor context.
Decisions Made in Draft PRD
- Plan includes both read side and write side actions.
- Do not include
scheduling.no_showunless explicitly added to scope. - Prefer no DB migration for v1; store reason/note/outcome in notes and/or audit details.
- Use existing designer grouping by
scheduling.*prefix. - Treat recurring scopes as
single | future | allfollowingIEditScope.
Decisions Confirmed Before Implementation
Confirmed by user on 2026-04-25:
scheduling.reassignv1 supports multiple technicians and emits oneAPPOINTMENT_ASSIGNEDper newly assigned user.scheduling.cancelrequiresuser_schedule:updatebecause it marks status canceled rather than deleting rows.- Workflow action event publishing follows the existing Scheduling action pattern: fail-soft/log rather than rollback/fail the workflow action.
- Private entries are redacted unless the actor is assigned or has
user_schedule:update. - Leave
scheduling.assign_userevent emission unchanged in this pass unless implementation discovers it blocks consistency.
Commands Run
pwd && git status --short && find ee/docs/plans -maxdepth 2 -type f | sed 's#^#/#' | head -80
rg -n "registerSchedulingActions|APPOINTMENT_|schedule|schedule_entries|schedule_entry|reschedule|NO_SHOW|appointment" shared ee server packages -g '!node_modules' -g '!**/.next/**'
find shared/workflow/runtime/actions/businessOperations -maxdepth 1 -type f -print
rg -n "publishWorkflowEvent|eventBus|createWorkflowEvent|SCHEDULE_ENTRY_|APPOINTMENT_CREATED|APPOINTMENT_RESCHEDULED|APPOINTMENT_ASSIGNED|APPOINTMENT_CANCELED|APPOINTMENT_COMPLETED|schedule_entries|schedule_entry_assignees|schedule_conflicts|recurr" shared server packages ee/server -g '!node_modules' -g '!**/.next/**'
rg -n "class ScheduleEntry|ScheduleEntry|scheduleEntry|schedule_entries|schedule_entry_assignees" server packages/scheduling shared -g '!node_modules' -g '!**/.next/**'
rg -n "scheduling\.assign_user|registerSchedulingActions|businessOperations" shared server ee packages -g '*test*' -g '!node_modules'
python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/scaffold_plan.py "Workflow Scheduling Actions" --slug workflow-scheduling-actions
Validation Commands for Plan
python3 /Users/roberisaacs/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-04-25-workflow-scheduling-actions
Likely Implementation Test Commands
Exact commands may change after implementation, but likely starting points are:
npm --prefix shared test -- workflow
npm --prefix server run test:integration -- scheduling
cd server && npx vitest run src/test/integration/scheduling/scheduleEntryRecurrence.integration.test.ts --coverage=false
Check package scripts before running broad test commands.
2026-04-25 — Post-main-merge Review
Merge State Observed
- Current branch
feature/workflows-scheduling-actionsis at1f44bf1b05, same asorigin/mainafter PR #2400 (feature/workflow-clients-actions) was merged. - Our scheduling plan remains untracked in
ee/docs/plans/2026-04-25-workflow-scheduling-actions/. - No scheduling implementation files have been changed yet in this branch.
Relevant New Main Additions Reviewed
shared/workflow/runtime/actions/businessOperations/clients.tsnow contains a much richer client action implementation.- New client action tests provide useful patterns for scheduling:
shared/workflow/runtime/actions/__tests__/registerClientActionsMetadata.test.tsshared/workflow/runtime/__tests__/workflowDesignerClientCatalogRuntime.test.tsshared/workflow/runtime/actions/__tests__/businessOperations.clients.db.test.tsshared/workflow/runtime/nodes/__tests__/actionCallClientSaveAsRuntime.test.ts
- Client actions added a local
publishWorkflowDomainEvent()helper that uses best-effort lazy import of@alga-psa/event-bus/publishers. This keeps shared-root tests stable while preserving runtime event publication behavior. - Main also added tenant-owned workflow definitions via
server/migrations/20260425200000_add_tenant_id_to_workflow_definitions.cjsand updatedshared/workflow/persistence/workflowDefinitionModelV2.tsto require tenant id for definition access.
Adjustments Made to This Scheduling Plan
- Updated PRD to follow the Client action implementation/testing patterns:
- picker metadata conversion tests via
zodToWorkflowJsonSchema; - runtime catalog grouping tests from real registrations;
- direct DB-backed action handler tests under the shared workflow runtime test tree;
- runtime
action.call+saveAssmoke coverage; - fail-soft lazy event publisher helper for
APPOINTMENT_*events.
- picker metadata conversion tests via
- Updated PRD action contracts so
entry_idinputs are non-empty strings rather than strict UUIDs. Rationale: existing recurrence virtual ids use<masterEntryId>_<timestamp>, so strict UUID schemas would block the requested recurrence-scope behavior. - Added
F031/T017for shared actor-resolution hardening against tenant-ownedworkflow_definitions. CurrentresolveRunActorUserId()joinsworkflow_definitionsby workflow id only; implementation should join by run tenant as well before relying on schedule permission checks. - Added
F032/T016to explicitly align scheduling action tests with the new Client action/runtime patterns. - Updated
T002to reject blank entry refs but allow virtual recurring entry refs.
New Implementation Watch-outs
- Do not copy
clients.tswholesale; use its patterns, not its domain assumptions. - Keep
scheduling.assign_userunchanged per confirmed scope, but account for the fact that Client actions now have event-publication parity and scheduling lifecycle actions should too. - If adding a shared publisher helper to
shared.ts, check whether moving the Client local helper is worth the extra scope. Current plan assumes a scheduling-local helper to avoid touching the merged Client action surface. - Add tests defensively: action registry is a singleton, so follow the Client tests' guard pattern (
if (!registry.get(...)) register...) to avoid duplicate registration failures.
2026-04-26 — Implementation Completed
Skills Used / Coordination
- Reviewed
brainstormingskill requirements and intentionally skipped full brainstorm gate because this turn already provided a completed PRD + feature/test implementation checklist and explicitly requested autonomous execution of that plan. - Followed existing Client workflow action patterns for registration metadata, fail-soft event publishing, runtime catalog checks, and node smoke tests.
Implemented Code
- Updated workflow actor resolution join to tenant-scope workflow definitions:
shared/workflow/runtime/actions/businessOperations/shared.tsresolveRunActorUserId()now joinsworkflow_definitionson bothworkflow_idandwr.tenant_id = wd.tenant_id.
- Replaced scheduling action runtime implementation with full lifecycle + read surface:
shared/workflow/runtime/actions/businessOperations/scheduling.ts- Added
scheduling.find_entry,scheduling.search_entries,scheduling.reschedule,scheduling.reassign,scheduling.cancel,scheduling.complete. - Kept existing
scheduling.assign_userbehavior as-is. - Added private-entry redaction policy (redact unless actor assigned or has
user_schedule:update;include_private_detailscontrols full visibility return forfind_entry). - Added read/write permission checks (
user_schedule:readfor read actions,user_schedule:updatefor lifecycle write actions). - Added recurrence-scope handling through
ScheduleEntry.update(...)with virtual-occurrence anchor patches forsingleon virtual ids. - Added conflict detection helper for reschedule that excludes target series and ignores canceled/completed/no-show statuses.
- Implemented
conflict_modebehavior:failthrowsCONFLICT.shiftshifts to earliest non-conflicting slot preserving duration.overridepersists and writes unresolvedschedule_conflictsrows.
- Added technician-role eligibility validation for reassignment.
- Added no-op reassignment behavior with
changed=false+ event suppression. - Added write audits for all new mutating actions.
- Added best-effort lazy
publishWorkflowEventhelper and emitted:APPOINTMENT_RESCHEDULEDAPPOINTMENT_ASSIGNED(one per newly assigned user)APPOINTMENT_CANCELEDAPPOINTMENT_COMPLETED
Added Tests
- Metadata/schema tests:
shared/workflow/runtime/actions/__tests__/registerSchedulingActionsMetadata.test.ts
- Runtime designer grouping test:
shared/workflow/runtime/__tests__/workflowDesignerSchedulingCatalogRuntime.test.ts
- Runtime node smoke (
action.call+saveAs):shared/workflow/runtime/nodes/__tests__/actionCallSchedulingSaveAsRuntime.test.ts
- DB-backed scheduling action tests:
shared/workflow/runtime/actions/__tests__/businessOperations.scheduling.db.test.ts
- Shared helper guard test for actor resolution:
shared/workflow/runtime/actions/__tests__/businessOperations.shared.actorResolution.db.test.ts
- Shared DB test utility for runtime action DB suites:
shared/workflow/runtime/actions/__tests__/_dbTestUtils.ts
Final Decisions Confirmed in Implementation (F030)
- Multi-assignee reassignment emits one
APPOINTMENT_ASSIGNEDper newly assigned user. - Cancel action permission uses
user_schedule:update(status mutation, no row deletion). - Workflow event publishing is fail-soft and does not roll back successful table writes/audit writes.
- Private entries are redacted unless actor is assigned or has
user_schedule:update. scheduling.assign_userevent behavior remains unchanged in this pass.
Validation Commands Run
cd shared && npx vitest run --coverage.enabled=false \
workflow/runtime/actions/__tests__/registerSchedulingActionsMetadata.test.ts \
workflow/runtime/__tests__/workflowDesignerSchedulingCatalogRuntime.test.ts \
workflow/runtime/nodes/__tests__/actionCallSchedulingSaveAsRuntime.test.ts \
workflow/runtime/actions/__tests__/businessOperations.scheduling.db.test.ts
cd shared && npx vitest run --coverage.enabled=false \
workflow/runtime/actions/__tests__/businessOperations.shared.actorResolution.db.test.ts
cd server && npx vitest run --coverage.enabled=false \
src/test/integration/scheduling/scheduleEntryRecurrence.integration.test.ts
Gotchas / Notes
@alga-psa/scheduling/models/scheduleEntrywas not export-resolvable under shared Vitest import-analysis; switched to direct monorepo source import path for runtime/test code in this branch.- Vitest coverage config mismatch in this environment required explicit
--coverage.enabled=false. - DB-backed runtime tests are intentionally using mocked
withTenantTransaction/requirePermissionwrappers for deterministic actor + permission simulation while still exercising real table mutations.