# Scratchpad — Workflow Time Actions - Plan slug: `2026-04-27-workflow-time-actions` - Created: `2026-04-27` ## What This Is Rolling notes for adding workflow-safe time-entry, time-sheet, and billing-readiness actions to the workflow action registry. ## Decisions - (2026-04-27) Scope option selected: core time entry actions, core time sheet actions, and readiness helpers. Timers and broad picker expansion can be deferred unless required by core flows. - (2026-04-27) Implementation approach selected: create workflow-safe helpers/services that preserve canonical time module behavior, then build workflow actions on top of those helpers. Do not continue direct DB writes that bypass time-entry business logic. - (2026-04-27) Existing `time.create_entry` should be treated as incomplete/prototype behavior and brought onto the canonical helper path rather than expanded as-is. - (2026-04-27) Picker scope decision (`F028`): keep phase-1 fixed picker expansion minimal to currently supported infrastructure (user/ticket + textarea metadata). Service/contract/time-entry/time-sheet/time-period dedicated fixed pickers are explicitly deferred; schema descriptions and dynamic-reference support remain the fallback for those IDs. ## Discoveries / Constraints - (2026-04-27) Current workflow action file: `shared/workflow/runtime/actions/businessOperations/time.ts`. - (2026-04-27) Existing `time.create_entry` directly inserts into `time_entries`, uses `billing_plan_id`, computes `work_date` from UTC, and does not attach to a time sheet or resolve `service_id`/`contract_line_id` behavior. - (2026-04-27) Canonical scheduling behavior lives primarily in `packages/scheduling/src/actions/timeEntryCrudActions.ts`, `timeSheetOperations.ts`, and `timeSheetActions.ts`. - (2026-04-27) Important canonical side effects include `service_id` validation, user timezone work-date computation, time-sheet period validation, default contract line resolution, bucket usage updates, project-task actual-hours updates, ticket/task resource updates, invoiced-entry guards, and change-request handling. - (2026-04-27) API time-entry service exists at `server/src/lib/api/services/TimeEntryService.ts`, but it has its own behavior and should not be blindly treated as the canonical source without reconciliation. - (2026-04-27) Workflow fixed pickers currently support board, client, contact, user, user-or-team, ticket, ticket-status, ticket-priority, ticket-category, ticket-subcategory, and client-location. They do not currently support service catalog, contract line, time entry, time sheet, time period, project task, interaction, or non-billable category resources. - (2026-04-27) Event schemas for `TIME_ENTRY_SUBMITTED` and `TIME_ENTRY_APPROVED` exist in `shared/workflow/runtime/schemas/timeEventSchemas.ts`, but current workflow-trigger publication appears limited; ticket time entry added publication is handled by `server/src/lib/api/services/timeEntryWorkflowEvents.ts`. - (2026-04-27) `packages/scheduling/src/services/bucketUsageService.ts` requires tenant inference via `createTenantKnex()` when transaction config does not carry tenant metadata; workflow runtime already has explicit tenant context, so directly calling that service from workflow helper would be fragile in shared Vitest DB tests. ## Commands / Runbooks - (2026-04-27) Initial investigation used `rg` across `server`, `shared`, `packages`, and `ee` for `time_entries`, `time_sheets`, `time.create_entry`, and workflow picker support. - (2026-04-27) Validate plan JSON after edits: `python scripts/validate_plan.py ee/docs/plans/2026-04-27-workflow-time-actions` if the validator supports folder input; otherwise run project JSON validation or `python -m json.tool` on each JSON file. ## Links / References - `shared/workflow/runtime/actions/businessOperations/time.ts` - `shared/workflow/runtime/actions/registerBusinessOperationsActions.ts` - `shared/workflow/runtime/designer/actionCatalog.ts` - `shared/workflow/runtime/jsonSchemaMetadata.ts` - `packages/scheduling/src/actions/timeEntryCrudActions.ts` - `packages/scheduling/src/actions/timeSheetOperations.ts` - `packages/scheduling/src/actions/timeSheetActions.ts` - `packages/scheduling/src/actions/timeEntryServices.ts` - `packages/scheduling/src/actions/timeEntryHelpers.ts` - `server/src/lib/api/services/TimeEntryService.ts` - `server/src/lib/api/schemas/timeEntry.ts` - `server/src/lib/api/services/timeEntryWorkflowEvents.ts` - `shared/workflow/runtime/schemas/timeEventSchemas.ts` - `ee/server/src/components/workflow-designer/WorkflowActionInputFixedPicker.tsx` ## Open Questions - Should time actions keep `time.create_entry` at version 1 with corrected semantics, add version 2, or add a new action id and deprecate the current action? - Should phase 1 include new fixed pickers for service, contract line, time entry, time sheet, and time period, or should these fields remain reference/manual UUID inputs initially? - Should this plan include publishing/trigger improvements for time-sheet submitted/approved and time-entry approval events, or remain action-only except for using existing events? ## Progress Log - (2026-04-27) Implemented workflow-safe create-entry domain boundary in `shared/workflow/runtime/actions/businessOperations/timeDomain.ts`. - New helper signature is explicit: `{ trx, tenantId, actorUserId, input }`. - Helper performs service/work-item existence checks, computes user-timezone work-date fields, resolves/creates time sheets by work date, and applies ticket/task side effects. - Output is normalized summary payload intended for workflow runtime consumers. - (2026-04-27) Replaced direct-write `time.create_entry` handler logic in `shared/workflow/runtime/actions/businessOperations/time.ts` to call `createWorkflowTimeEntry(...)`. - Decision: keep `time.create_entry@v1` and migrate semantics in place; retain `billing_plan_id` as a compatibility alias to `contract_line_id`. - Added structured domain error mapping via `WorkflowTimeDomainError` -> runtime `throwActionError` categories. - Added normalized output schema: `time_entry` object with ids, duration/billable minutes, work date/timezone, sheet/service/contract references, and status fields. - (2026-04-27) Added DB-backed runtime action test: `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` covering T001 scenario. - Test validates service requirement, user timezone work-date calculation (`America/Los_Angeles`), automatic time-sheet association, and normalized output/row persistence. - (2026-04-27) Implemented `F006` bucket usage side effects in workflow create-entry path in `shared/workflow/runtime/actions/businessOperations/timeDomain.ts`. - Added explicit-tenant bucket period resolution (`client_billing_cycles` first, then contract-line anchored frequency period). - Added bucket usage record upsert for `(tenant, client, contract_line, service, period)` and minutes/overage delta updates gated by `Bucket` overlay configuration. - Hooked create-entry to apply delta from `billable_duration` after entry insert. - (2026-04-27) Added DB-backed runtime test case for `T002` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Uses billing fixtures (`createFixedPlanAssignment`, `createBucketOverlayForPlan`) to seed a bucket-backed contract line. - Verifies default contract-line selection and `bucket_usage.minutes_used` increment from workflow `time.create_entry`. - (2026-04-27) Implemented workflow `time.update_entry` and `time.delete_entry` actions in `shared/workflow/runtime/actions/businessOperations/time.ts` backed by new domain helpers. - New domain helpers: `updateWorkflowTimeEntry(...)` and `deleteWorkflowTimeEntry(...)` in `shared/workflow/runtime/actions/businessOperations/timeDomain.ts`. - Update behavior includes invoiced guard, recomputation of work-date/timezone + billable minutes, timesheet re-association, bucket usage rebalance (`-old +new`), project-task actual-hours recalc (old/new task), and ticket/project-task assignment side effects. - Delete behavior includes invoiced guard, bucket usage decrement, and project-task actual-hours recalc. - (2026-04-27) Added DB-backed runtime test case for `T003` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Creates a project-task-linked entry, updates duration via workflow action, and deletes via workflow action. - Asserts `project_tasks.actual_hours` transitions `30 -> 90 -> 0`. - (2026-04-27) Implemented `time.get_entry` (`F009`) and `time.find_entries` (`F010`) in workflow runtime. - Added domain helpers `getWorkflowTimeEntry(...)` and `findWorkflowTimeEntries(...)`. - `find_entries` supports bounded filters across user/work-item/client/service/contract/status/date/time/invoiced scopes and returns aggregate summary totals. - Registered new actions in `shared/workflow/runtime/actions/businessOperations/time.ts` with read permissions and normalized output schemas. - (2026-04-27) Added DB-backed runtime test case for `T005` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Verifies `time.get_entry` normalized response shape. - Verifies `time.find_entries` filtered list + aggregate totals. - Verifies tenant-scoped isolation by asserting cross-tenant entry lookup returns `NOT_FOUND`. - (2026-04-27) Added DB-backed runtime test case for `T004` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Marks a project-task time entry as invoiced, then verifies `time.update_entry` and `time.delete_entry` reject with validation errors. - Verifies project-task actual-hours and bucket-usage totals remain unchanged after rejected mutations. - (2026-04-27) Implemented entry approval actions in workflow runtime: - `time.set_entry_approval_status` for `DRAFT`/`SUBMITTED`/`APPROVED`/`CHANGES_REQUESTED` transitions. - `time.request_entry_changes` convenience bulk action. - Domain helpers added in `timeDomain.ts`: `setWorkflowTimeEntryApprovalStatus(...)` and `requestWorkflowTimeEntryChanges(...)`, including `time_entry_change_requests` creation when change-request comments are supplied. - (2026-04-27) Added DB-backed runtime test case for `T006` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Verifies state transitions through submitted/approved/changes-requested and confirms change-request row creation (single + bulk convenience action). - (2026-04-27) Implemented timesheet lookup actions (`F015`/`F016`/`F017`) with shared domain helpers: - `findOrCreateWorkflowTimeSheet(...)` - `getWorkflowTimeSheet(...)` - `findWorkflowTimeSheets(...)` - Helpers provide period metadata, entry/comment counts, and billable/total minute summaries. - (2026-04-27) Added DB-backed runtime test case for `T007` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Verifies `find_or_create_timesheet`, `get_timesheet`, and `find_timesheets` for representative period/date/status filters. - Verifies summary fields and comments payload shape. - (2026-04-27) Implemented timesheet mutation actions (`F018`–`F022`) in workflow runtime: - `time.submit_timesheet` - `time.approve_timesheet` - `time.request_timesheet_changes` - `time.reverse_timesheet_approval` - `time.add_timesheet_comment` - Domain helpers enforce core state transitions and invoiced-entry guard on reverse. - (2026-04-27) Added DB-backed runtime tests for `T008` and `T009` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - `T008`: submit propagates `SUBMITTED` to sheet + entries. - `T009`: approve/request-changes/add-comment/reverse flow behavior with invoiced-entry reopen guard. - (2026-04-27) Implemented readiness helpers (`F023`/`F024`/`F025`) in workflow runtime: - `time.summarize_entries` - `time.find_billing_blockers` - `time.validate_entries` - Added grouping support, blocker categorization/explanations, and validation pass/fail summaries. - (2026-04-27) Added DB-backed runtime tests for `T010` and `T011` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - `T010`: blocker category detection and validation failure summary. - `T011`: summary totals and grouped output verification. - (2026-04-27) Added explicit time action registration/metadata test file: `shared/workflow/runtime/actions/__tests__/registerTimeActionsMetadata.test.ts`. - `T013`: verifies time action registration completeness, stable ids/versions, idempotency metadata, and Time catalog grouping. - `T014`: verifies user/ticket picker metadata and textarea editor metadata survive Zod->JSON schema conversion for representative time action inputs. - (2026-04-27) Added workflow JSON-schema metadata annotations in `time.ts` for high-value user/ticket picker fields and long-text comment/note inputs. - (2026-04-27) Added DB-backed runtime permission test case `T012` in `shared/workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts`. - Verifies structured `PERMISSION_DENIED` errors for denied `timeentry:create`, `timeentry:read`, and `timesheet:approve` paths. - (2026-04-27) Implemented workflow audit writes for additional mutating time actions in `time.ts`: - `time.find_or_create_timesheet` - `time.submit_timesheet` - `time.approve_timesheet` - `time.request_timesheet_changes` - `time.reverse_timesheet_approval` - `time.add_timesheet_comment` - (2026-04-27) Added DB-backed runtime tests: - `T015`: verifies mutating time actions emit workflow run audit rows with action metadata and changed IDs. - `T016`: verifies `time.create_entry` compatibility alias (`billing_plan_id`) still routes through canonical service/time-sheet/work-date behavior. - (2026-04-27) Compatibility decision documentation status (`F032`): - Keep `time.create_entry@v1` and preserve input compatibility via `billing_plan_id` alias while enforcing canonical semantics. - Behavior now always runs through workflow-safe helper path with service validation, timezone work-date, timesheet association, and side effects. ## Commands / Verification (This Pass) - Ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/__tests__/workflowDesignerActionCatalog.test.ts` (pass) - Attempted: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Blocked locally due DB connection refusal at `127.0.0.1:57432` / `::1:57432`. - Test file compiles/loads, but DB-backed execution requires local test Postgres availability. - Attempted after F006/T002 changes: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Still blocked locally by the same test DB connection refusal (`127.0.0.1:57432`). - Attempted after update/delete action + T003 changes: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by local connection refusal (`127.0.0.1:57432`). - Attempted after get/find actions + T005 changes: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after T004 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after approval-action + T006 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after timesheet lookup actions + T007 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after timesheet mutation actions + T008/T009 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after readiness helper + T010/T011 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted: `npx tsc -p shared/tsconfig.json --noEmit` - Fails due pre-existing workspace TS config/module issues outside this feature area (`@alga-psa/sla/types`, alias `@/lib/*`, and existing `server/test-utils/dbReset.ts` declaration-order error). - Ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/__tests__/workflowDesignerActionCatalog.test.ts` (pass after each action-registration expansion). - Ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/registerTimeActionsMetadata.test.ts` (pass) - Attempted after permission test additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Attempted after T015/T016 additions: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` - Compile/import succeeds, but DB-backed execution remains blocked by the same local connection refusal (`127.0.0.1:57432`). - Ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/registerTimeActionsMetadata.test.ts workflow/runtime/__tests__/workflowDesignerActionCatalog.test.ts` (pass) - Re-ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/registerTimeActionsMetadata.test.ts workflow/runtime/__tests__/workflowDesignerActionCatalog.test.ts` (pass, 2026-04-27 final verification before completion) - Re-ran: `npx vitest run --config shared/vitest.config.ts workflow/runtime/actions/__tests__/businessOperations.time.db.test.ts` (still blocked by local DB connection refusal at `::1/127.0.0.1:57432`; tests skipped due suite setup failure) - Verified remaining checklist state with `jq`: `features.json` false count = `0`, `tests.json` false count = `0`. ## Gotchas - `@alga-psa/billing` subpath imports used in server packages are not exported for this shared Vite test runtime. To keep workflow-runtime tests executable, contract-line defaulting and bucket-usage linkage were intentionally deferred to subsequent features (`F005`/`F006`) rather than forcing brittle deep imports. - (2026-04-27) Implemented default contract-line resolution in workflow helper (`F005`) without depending on non-exported billing package subpaths. - Added tenant-scoped eligible-contract query with effective-date filtering and deterministic selection fallback (single eligible line, or single bucket-overlay candidate). - `time.create_entry` now assigns `contract_line_id` automatically when omitted and client/service context can resolve a default. - (2026-04-27) Bucket usage side-effect implementation in workflow helper intentionally keeps explicit tenant-scoped SQL in `timeDomain.ts` instead of importing `@alga-psa/billing`/`@alga-psa/scheduling` internals to avoid export-boundary and tenant-resolution brittleness in shared runtime tests. ## Review Follow-up Fixes - (2026-04-27) Removed workflow time-sheet writes to non-existent `time_sheets.created_at` / `time_sheets.updated_at` columns; the current schema only has `submitted_at`, `approved_at`, and `approved_by` on `time_sheets`. - (2026-04-27) Fixed TypeScript aggregate typing in `findWorkflowTimeEntries` so shared typecheck no longer reports `timeDomain.ts` aggregate property errors. - (2026-04-27) Added workflow-time subject guards for acting on behalf of another user, self-approval denial, and scoped find/read helpers. The guard uses `timesheet:read_all` or manager/reporting relationship for cross-user access after the action-level permission check. - (2026-04-27) Reworked `summarizeTimeSheet` to aggregate entries and comments separately to avoid entry-minute inflation when a sheet has multiple comments. - (2026-04-27) Fixed `time.update_entry` detach semantics so `attach_to_timesheet: false` actually clears the sheet instead of retaining the existing sheet. - (2026-04-27) Removed the misleading ticket picker from polymorphic `link.id`; ticket-specific filters still use the ticket picker. - (2026-04-27) Validation run: `npx vitest run shared/workflow/runtime/actions/__tests__/registerTimeActionsMetadata.test.ts --config shared/vitest.config.ts` passed. - (2026-04-27) Validation run: `npx eslint shared/workflow/runtime/actions/businessOperations/time.ts shared/workflow/runtime/actions/businessOperations/timeDomain.ts` passed. - (2026-04-27) Validation run: `npx tsc --noEmit --project shared/tsconfig.json` still fails on pre-existing unrelated path/dbReset errors, but no longer reports `timeDomain.ts` errors. - (2026-04-27) Updated DB-backed workflow time tests to grant the workflow actor real MSP time permissions so the new subject-scope helper checks can run under migrated schema instead of relying only on the mocked `requirePermission` path.