Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
13 KiB
Scratchpad — Workflow Client Actions
- Plan slug:
2026-04-25-workflow-client-actions - Created:
2026-04-25
What This Is
Working notes for adding missing Client module workflow actions to Workflow Runtime V2.
Decisions
- (2026-04-25) Draft action ids use
clients.create,clients.update,clients.archive,clients.delete,clients.duplicate,clients.add_tag,clients.assign_to_ticket,clients.add_note, andclients.add_interaction. Rationale: keeps all requested actions in the Client module and allows current catalog prefix grouping to work automatically. - (2026-04-25) Resolved: use
clients.updateas the implementation id for EDIT client, with UI label "Edit Client". Rationale: action ids are durable runtime/API-style contracts while UI labels can match user-facing wording. - (2026-04-25) Resolved:
clients.add_noteappends to the client notes document, not the generic CRM activity note/interactions path. Rationale:crm.create_activity_notealready exists and usesinteractions; the requested action is explicitly for the Client module, and "add" should not replace existing notes. - (2026-04-25) Resolved:
clients.deleteis validated hard delete and requiresconfirm: true;clients.archiveis added now as a separate archive/deactivate action. Rationale: delete and archive are materially different operations and should not be overloaded, and hard delete should require explicit destructive-action confirmation.
Discoveries / Constraints
- (2026-04-25) Existing client workflow actions are in
shared/workflow/runtime/actions/businessOperations/clients.tsand currently register onlyclients.findandclients.search. - (2026-04-25) Business operations registration path is already wired:
shared/workflow/runtime/actions/registerBusinessOperationsActions.tscallsregisterClientActions(). - (2026-04-25) The designer catalog already maps
clients.*actions into the built-in Client group viashared/workflow/runtime/designer/actionCatalog.ts; no new catalog group should be needed. - (2026-04-25) Existing helper patterns are in
shared/workflow/runtime/actions/businessOperations/shared.ts:withTenantTransaction,requirePermission,writeRunAudit,throwActionError,rethrowAsStandardError,actionProvidedKey,uuidSchema, andisoDateTimeSchema. - (2026-04-25)
shared/models/clientModel.tshas shared create/update/get helpers, butserver/src/lib/api/services/ClientService.tshas richer delete behavior, tag handling, inactive-client side effects, and workflow event publishing. - (2026-04-25)
packages/clients/src/actions/clientNoteActions.tsshows current client notes document behavior usingclients.notes_document_id,createBlockDocument, andupdateBlockContent. - (2026-04-25)
packages/clients/src/actions/interactionActions.tsandpackages/clients/src/models/interactions.tsshow current interaction creation/default-status/event behavior. - (2026-04-25) Existing
crm.create_activity_notecreates aninteractionsrow for target types includingclient; this may overlap conceptually withclients.add_noteunless the latter is explicitly defined as client notes-document behavior. - (2026-04-25)
tickets.link_entitiesdoes not currently support linking clients to tickets;clients.assign_to_ticketshould likely updatetickets.client_iddirectly rather than using the generic ticket link table. - (2026-04-25) No existing duplicate-client implementation was found in quick code search; duplicate semantics need scope confirmation.
Commands / Runbooks
- (2026-04-25) Context reads/searches used while drafting:
find ee/docs/plans -maxdepth 2 -type f | sort | tail -80rg -n "register.*Actions|clients.find|clients.search|actionCatalog|x-workflow-picker-kind" shared ee server -g'*.ts' -g'*.tsx'rg -n "class ClientModel|ClientModel|createClient|updateClient|deleteClient|interaction|tag_mappings" server/src shared packages -g'*.ts' -g'*.tsx'rg -n "Duplicate Client|duplicateClient|client.*duplicate" packages/clients server/src -g'*.ts' -g'*.tsx'
Links / References
shared/workflow/runtime/actions/businessOperations/clients.tsshared/workflow/runtime/actions/businessOperations/tickets.tsshared/workflow/runtime/actions/businessOperations/crm.tsshared/workflow/runtime/actions/businessOperations/shared.tsshared/workflow/runtime/actions/registerBusinessOperationsActions.tsshared/workflow/runtime/designer/actionCatalog.tsshared/models/clientModel.tsserver/src/lib/api/services/ClientService.tspackages/clients/src/actions/clientNoteActions.tspackages/clients/src/actions/interactionActions.tspackages/clients/src/models/interactions.tsshared/workflow/runtime/actions/__tests__/registerTicketActionPickerMetadata.test.tsserver/src/test/unit/workflowTicketAssignmentModelRuntime.test.ts
Open Questions
- Resolved: EDIT client action id is
clients.update, with UI label "Edit Client". - Resolved:
clients.add_noteappends to the client notes document; it does not replace notes and does not create an activity note interaction. - Resolved:
clients.duplicatecopies safe core profile fields, copies tags by default, supports optionalcopy_locations, and leaves contacts/notes out of v1. - Resolved:
clients.deleteis validated hard delete with requiredconfirm: true; addclients.archivenow for archive/inactivate semantics. - Resolved:
clients.assign_to_ticketpreserves omitted contact/location fields; explicitnullclears them, and non-null provided values must belong to the selected client. - Resolved:
clients.add_interactionuses the workflow actor asuser_idin v1. Future versions may add a permission-gated user override after target-user validation and audit semantics are designed.
Implementation Log
-
(2026-04-25) Implemented Client runtime action expansion in
shared/workflow/runtime/actions/businessOperations/clients.ts:- Added and registered all requested mutating actions:
clients.create,clients.update,clients.archive,clients.delete,clients.duplicate,clients.add_tag,clients.assign_to_ticket,clients.add_note,clients.add_interaction. - Added picker metadata helper usage for client/ticket/contact/location fields in new action schemas.
- Added idempotency metadata (
actionProvided) for create/duplicate/add_tag/add_note/add_interaction. - Added audit logging (
writeRunAudit) across mutating handlers. - Added standardized error classification pathways via
throwActionErrorandrethrowAsStandardErrorin mutation handlers.
- Added and registered all requested mutating actions:
-
(2026-04-25) Implemented create/update/archive/delete semantics:
clients.create: tenant-scoped create, optional location bootstrap from address/phone/email inputs, optional initial tags, and client summary output.clients.update: non-empty patch, changed-field reporting, tenant-scoped mutation, and inactive-side-effect deactivation for contacts/client users.clients.archive: idempotent inactive transition with no-op behavior when already inactive.clients.delete:confirm: trueguard, default-client protection, expliciton_not_foundbehavior, dependency-validated deletion path viadeleteEntityWithValidation, and cleanup of client-owned artifacts.
-
(2026-04-25) Implemented duplicate/tag/ticket-assignment/notes/interactions:
clients.duplicate: safe profile duplication with required new client name,copy_tagsdefault true, and opt-incopy_locations.clients.add_tag: tag definition upsert + idempotent tag mappings with added/existing counts and summaries.clients.assign_to_ticket: validates ticket/client existence, validates provided contact/location belongs to selected client, preserves omitted contact/location, and supports explicitnullclear semantics.clients.add_note: append-only note block behavior on client notes document with create-if-missing path.clients.add_interaction: validates optional contact/ticket relationships, defaults interaction status when omitted, and always uses workflow actor asuser_id.
-
(2026-04-25) Added workflow event publication parity in runtime action handlers where feasible:
CLIENT_CREATEDon create.CLIENT_UPDATEDon update when effective updated fields exist.CLIENT_ARCHIVEDon first archive transition.NOTE_CREATEDwhen add-note creates a new notes document.INTERACTION_LOGGEDon add-interaction.- Event publishing is implemented as best-effort lazy import (
publishWorkflowDomainEvent) to keep shared runtime tests stable when event-bus module resolution is unavailable in test-only contexts.
-
(2026-04-25) Added unit coverage for registration/catalog/picker metadata:
shared/workflow/runtime/actions/__tests__/registerClientActionsMetadata.test.tsshared/workflow/runtime/__tests__/workflowDesignerClientCatalogRuntime.test.ts- Coverage scope:
- action registration IDs/labels/side-effect/idempotency metadata (
T001) - Client group catalog exposure from runtime registrations (
T002) - picker metadata on assign/note/interaction schemas (
T003)
- action registration IDs/labels/side-effect/idempotency metadata (
Verification
-
(2026-04-25) Build/compile validation:
npm run -s build:shared
-
(2026-04-25) Targeted unit tests (shared runtime root):
npx vitest run --root shared workflow/runtime/actions/__tests__/registerClientActionsMetadata.test.ts workflow/runtime/__tests__/workflowDesignerClientCatalogRuntime.test.ts
-
(2026-04-25) Follow-up hardening: switched workflow-event publishing in runtime client actions to lazy dynamic import helper (
publishWorkflowDomainEvent) to avoid shared-root test module-resolution failures while preserving runtime publication behavior. -
(2026-04-25) Follow-up hardening: standardized not-found/internal error signaling in helper paths (
ensureClientExists,ensureTicketExists, default interaction status lookup) to usethrowActionErrorcategories/codes directly.
Implementation Log — 2026-04-25 (Checkpoint 2)
- Expanded DB-backed workflow client action coverage in
shared/workflow/runtime/actions/__tests__/businessOperations.clients.db.test.tsto cover:T004create + tags + action-provided idempotency-key fallback behaviorT005update + cross-tenant not-foundT006delete guardrails (confirm, default-client guard, dependency conflict)T007duplicate copy semantics (tags by default, optional locations, contacts excluded)T008add_tag idempotent no-duplicate behaviorT009assign_to_ticket preserve/clear semanticsT010assign_to_ticket relationship validation for contact/locationT011add_note create-if-missing + append semanticsT012add_interaction default status + relationship validation + actor ownershipT013permission-denied coverage for all mutating client actionsT015archive semantics + associated contact/client-user deactivation and idempotent second run
- Left
T014pending because it requires runtime-levelaction.callsmoke execution withsaveAsand downstream expression usage (separate harness from direct action handler DB tests). - Marked
F024implemented because DB-backed mutation + guard coverage is now added across representative success/failure paths.
Implementation Log — 2026-04-25 (Checkpoint 2, code fix)
- Updated
ensureClientTagMappingsinshared/workflow/runtime/actions/businessOperations/clients.tsto avoid using insert-error control flow for duplicate mappings/definitions in the same transaction. - Rationale: catching
23505inside a PostgreSQL transaction still leaves the transaction aborted; subsequent audit writes fail with "current transaction is aborted". - New behavior: pre-check existing mappings first, insert only when missing, and classify duplicates as
existingwithout relying on failed inserts.
Commands / Verification — 2026-04-25 (Checkpoint 2)
npx vitest run --root shared workflow/runtime/actions/__tests__/businessOperations.clients.db.test.ts- Result: passing (10 tests).
npx vitest run --root shared workflow/runtime/actions/__tests__/registerClientActionsMetadata.test.ts workflow/runtime/__tests__/workflowDesignerClientCatalogRuntime.test.ts- Result: passing (3 tests).
npm run -s build:shared- Result: passing.
- Added runtime smoke coverage for
T014withshared/workflow/runtime/nodes/__tests__/actionCallClientSaveAsRuntime.test.ts.- Uses real
action.callandtransform.assignnode handlers. - Invokes representative
clients.createaction id (handler stubbed in-test for deterministic runtime behavior). - Verifies
saveAsoutput becomes available undervars.<saveAs>and is consumed by downstream expression assignments.
- Uses real
- Verification:
npx vitest run --root shared workflow/runtime/nodes/__tests__/actionCallClientSaveAsRuntime.test.ts workflow/runtime/actions/__tests__/businessOperations.clients.db.test.ts