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

35 KiB

SCRATCHPAD — Workflow Project Actions

  • Slug: workflow-project-actions
  • Date: 2026-04-27
  • Status: Discovery / PRD convergence

Initial request

Add workflow actions for project-management entities:

  • FIND task / phase / project
  • MOVE task (to different status + phase, or project + phase + status)
  • RENAME task / phase / project
  • ASSIGN task
  • DUPLICATE task
  • DELETE task / phase / project
  • LINK TICKET to task
  • ADD TAG to task or project
  • EDIT task / phase / project description

Context from request

Workflow actions are server-registered through shared/workflow/runtime/registries/actionRegistry.ts, commonly in shared/workflow/runtime/actions/registerBusinessOperationsActions.ts and domain files under shared/workflow/runtime/actions/businessOperations/.

Action schemas are Zod schemas converted to JSON Schema and consumed by the workflow designer. Picker/editor metadata can be attached via schema descriptions and normalized into ActionInputField.editor. Picker-backed fields should use x-workflow-editor where possible, with legacy picker metadata still supported.

Repository discoveries

  • Existing project workflow action file: shared/workflow/runtime/actions/businessOperations/projects.ts.
  • Existing registered project action: projects.create_task.
  • It already validates project, optional phase, optional project_task status, optional user/team assignee, optional ticket link, writes audit, and returns task URL/status metadata.
  • Existing ticket action code has a generic ticket-to-entity pattern in shared/workflow/runtime/actions/businessOperations/tickets.ts, including project and project_task entity types.
  • Plans live under ee/docs/plans/YYYY-MM-DD-<slug>/ with PRD.md, features.json, tests.json, and SCRATCHPAD.md.

Open questions

  • Need decide whether FIND actions are exact-ID lookup actions, criteria/search actions, or both.
  • Need define destructive delete semantics for phases/projects/tasks.
  • Need define duplicate semantics for tasks (comments/checklists/tags/ticket links/time entries/subtasks?).
  • Need confirm picker support needed for project/phase/task/status/tag resources in workflow fixed-value inputs.

Decisions

  • FIND scope: use both exact lookup and search/list-style actions, following existing workflow action patterns.

  • Pattern fidelity is a primary design constraint. Existing examples:

    • clients.find: exact lookup by id/name/external ref with on_not_found and nullable output.
    • clients.search: query + filters + pagination, returns array, first result, page metadata, total.
    • contacts.find / contacts.search: same split.
    • tickets.find: exact lookup by id/number/external ref with optional include flags.
  • Proposed PRD direction: project entities should mirror that split rather than inventing separate get naming unless the registry already uses it elsewhere.

  • Delete semantics decision: target soft/archive semantics where the project domain supports them; use hard delete only where the domain's existing behavior is genuinely hard delete. Preserve existing domain guards and avoid workflow-specific cascade behavior.

Delete/move/duplicate implementation fidelity discoveries

  • User decision: delete semantics should mirror existing project UI/domain behavior exactly.
  • Existing task delete UI action (packages/projects/src/actions/projectTaskActions.ts) checks project:delete, validates project read access, refuses delete when associated time_entries exist, deletes ticket links, deletes checklist items, then deletes project_tasks.
  • Existing phase delete UI action (packages/projects/src/actions/projectActions.ts) checks project:delete, validates project read access, then calls ProjectModel.deletePhase.
  • Existing project delete UI action (packages/projects/src/actions/projectActions.ts) uses deleteEntityWithValidation('project', ...), cleans up project tags, task tags under phases, project ticket links, email reply tokens, then deletes through ProjectModel.delete.
  • Existing API service (server/src/lib/api/services/ProjectService.ts) has simpler hard-delete methods, but workflow plan should prefer UI/domain action semantics when they are richer.
  • Existing task move UI action: moveTaskToPhase supports target phase, optional target status mapping, optional target project, optional before/after positioning. It remaps status mappings on same-project phase moves and cross-project moves if no explicit target status is provided, regenerates WBS/order key, and updates ticket link project/phase references.
  • Existing task duplicate UI action: duplicateTaskToPhase supports target phase, optional new status mapping, and options for primary assignee, additional assignees, checklist, and ticket links. It appends (Copy), resets actual hours to 0, creates task, optionally copies related records, and emits workflow events.

Update action decision

  • User decision: use generic update actions, not separate narrow rename/edit-description actions.
  • PRD direction:
    • projects.update covers project name and description edits, plus only other fields intentionally included by this plan.
    • projects.update_phase covers phase name and description edits, plus only other fields intentionally included by this plan.
    • projects.update_task covers task title/name, description, assignment, status, phase/project move-adjacent fields only if they do not conflict with the dedicated move action. Prefer dedicated projects.move_task for phase/project/status relocation because existing domain logic is non-trivial.
  • Avoid palette/action sprawl for rename_* and edit_*_description variants.

Assignment action decision

  • User decision: use a dedicated projects.assign_task action, matching tickets.assign fidelity.
  • PRD direction:
    • Keep task assignment out of generic projects.update_task except possibly preserving current assignment when other fields update.
    • projects.assign_task should follow tickets.assign shape where practical: required assignment target, optional reason/comment if useful, no_op_if_already_assigned default true, output task id/assigned target/updated timestamp.
    • Existing project task model supports assigned_to primary user and additional task resources. Need decide if workflow assignment supports only primary user in v1 or primary + additional users.

Task assignment model decision

  • User decision: projects.assign_task should support primary user + additional users.
  • PRD direction:
    • Primary assignee maps to project_tasks.assigned_to.
    • Additional users map to task resource/additional assignment records, matching existing project task UI/domain behavior.
    • Do not invent team assignment unless existing project task resource semantics support it; the first-class workflow task assignment model is user-centric.
    • Provide no-op behavior when primary and additional users already match, mirroring tickets.assign.

Tag behavior discovery/decision

  • User instruction: look at contact workflow/tag behavior and match it.

  • There is no dedicated contacts.add_tag workflow action today; contact/client workflow search actions filter by existing tag text through tag_mappings + tag_definitions joins.

  • Existing ticket workflow create/update tag handling creates missing tag_definitions, inserts mappings, treats duplicate mappings as no-op, and normalizes/uniquifies trimmed tag text.

  • Existing generic tag domain action createTag also get-or-creates tag definitions, requires entity update permission, requires tag:create only when the definition is new, creates the mapping, and publishes tag events.

  • createTagsForEntityWithTransaction get-or-creates definitions and continues on per-tag failures; this is used by imports including project task import.

  • PRD direction: projects.add_tag should match existing tag application semantics: trim/validate tag text, get-or-create tag definition for tagged type (project or project_task), insert mapping idempotently/no-op on duplicate, use existing color generation/definition colors, and respect tag permissions when practical.

  • Need be careful that workflow runtime direct DB implementation should either reproduce permission requirements or call shared tag-domain helper if callable from workflow runtime.

  • Follow-up search for user-mentioned Add Tag to Contact: current worktree search did not find a dedicated workflow contact add-tag action in shared/workflow/runtime/actions/businessOperations/contacts.ts or elsewhere under workflow runtime. It may be in another branch/module or not present in this worktree.

  • Concrete tag behavior found in packages/tags/src/actions/tagActions.ts#createTag: validates non-empty tag text, trims, max 50 chars, character whitelist, requires target entity update permission, requires tag:create only if creating a new definition, get-or-creates tag_definitions, inserts tag_mappings, publishes TAG_DEFINITION_CREATED and TAG_APPLIED, and returns the mapping id as tag_id for compatibility.

Tag action behavior decision/discovery

  • User direction: match the existing workflow contact module's contacts.add_tag behavior.
  • Existing contacts.add_tag behavior:
    • Action id: contacts.add_tag.
    • Input: contact_id, tags: string[], optional idempotency_key.
    • Side-effectful with idempotency: { mode: 'actionProvided', key: actionProvidedKey }.
    • Requires { resource: 'contact', action: 'update' }.
    • Ensures the contact exists.
    • Normalizes/uniques tag text.
    • Creates missing tag definitions for tagged_type: 'contact' with generated colors.
    • Inserts tag mappings idempotently via conflict ignore.
    • Returns added, existing, added_count, existing_count.
    • Writes run audit.
  • PRD direction:
    • Add project/task tag actions should create missing tag definitions and idempotently attach mappings, not fail on missing tags.
    • Match output shape and idempotency behavior from contacts.add_tag.
    • Use resource permissions appropriate to the target entity (project:update; task likely project update after project/task read scoping, matching existing project task actions).

Tag action shape decision

  • User decision: two resource-specific actions.
  • PRD direction:
    • projects.add_tag attaches tags to a project.
    • projects.add_task_tag attaches tags to a project task.
    • Both mirror contacts.add_tag behavior and output shape.
  • User decision: workflow task-ticket link should write both project_ticket_links and ticket_entity_links.
  • PRD direction:
    • Add action likely named projects.link_ticket_to_task.
    • Validate task, its phase/project, and ticket exist.
    • Require appropriate project update and ticket read permissions, matching existing UI behavior plus workflow business action conventions.
    • Insert project_ticket_links idempotently/best-effort where duplicate constraints exist.
    • Insert/ensure ticket_entity_links with entity_type: 'project_task', entity_id: task_id, link_type: 'project_task', and metadata containing project/phase IDs, matching projects.create_task precedent.
    • Return IDs and counts/created-vs-existing indicators if practical.

Picker/editor support decision

  • User decision: hybrid picker support.
  • PRD direction:
    • Add workflow fixed-value picker/editor support for project, phase, project task, and project task status/status mapping where needed.
    • Keep tag entry as free-text string array, matching contacts.add_tag; no tag picker in this plan.
    • Use dependency metadata so phase/task/status pickers can depend on selected project/phase as applicable.

PRD approval

  • User approved the PRD scope on 2026-04-27.
  • Estimated feature list size: medium/large workflow action expansion, roughly 60-75 atomic features because it spans handler schemas, action behavior, permissions/audit, tag/link helpers, and designer picker support.
  • Test plan should be Pareto-focused: DB-backed action handler coverage for representative read/update/mutation/destructive paths, plus picker metadata/designer resource coverage.

Implementation checkpoint — 2026-04-27 (Find/Search foundation)

Completed features

  • F001: Added shared workflow schemas in shared/workflow/runtime/actions/businessOperations/projects.ts for:

    • projectSummarySchema
    • phaseSummarySchema
    • taskSummarySchema
    • statusMappingSummarySchema
    • tagResultSchema
    • assignmentResultSchema
    • linkResultSchema Rationale: establishes canonical output schema contracts for new action family.
  • F002: Added reusable entity/context loaders with standardized NOT_FOUND errors:

    • ensureProjectExists
    • ensurePhaseExists
    • ensureTaskContext
    • ensureTicketExists
    • ensureStatusMappingExists Rationale: avoid repeated ad-hoc lookup/error logic across action handlers.
  • F003: Added reusable permission + narrowing helpers:

    • requireProjectReadPermission
    • requireProjectUpdatePermission
    • requireProjectDeletePermission
    • canReadProject / assertProjectReadable Rationale: centralizes project read/auth narrowing behavior and future-proofs update/delete action implementation.
  • F004: Added project picker metadata helpers and kinds:

    • picker kinds: project, project-phase, project-task, project-task-status
    • helper: withWorkflowPicker Rationale: fixed-value mode can render dependency-aware project entity pickers.
  • F005 + F006: Implemented projects.find schema + handler:

    • supports project_id, exact name, optional external_ref, on_not_found
    • enforces tenant scoping + project read permission + auth narrowing
    • returns nullable project summary output
  • F007 + F008: Implemented projects.search schema + handler:

    • query + filters + pagination
    • deterministic ordering + first_project + total
    • authorization-filtered results
  • F009 + F010: Implemented projects.find_phase schema + handler:

    • supports phase_id or project-scoped exact name
    • supports on_not_found
    • enforces project read auth via phase->project context validation
  • F011 + F012: Implemented projects.search_phases schema + handler:

    • supports project scope, query, filters, pagination
    • deterministic ordering + first_phase + total
    • auth-filtered by parent project visibility
  • F013 + F014: Implemented projects.find_task schema + handler:

    • supports task_id or scoped exact task name
    • supports on_not_found
    • validates task->phase->project context and project read authorization
  • F015 + F016: Implemented projects.search_tasks schema + handler:

    • supports query + project/phase/status mapping/status/assignee/tag filters
    • returns tasks, first_task, page metadata, total
    • tenant-scoped + project-auth filtered

Completed tests

  • T001: Added shared/workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts

    • validates registration, labels, side-effect/idempotency metadata, category
    • validates picker metadata presence and dependencies for project/phase/task/status fields
  • T002 + T003 + T004: Added DB-backed tests in shared/workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

    • covers projects.find + projects.search happy paths, not-found behavior, pagination/tenant scoping
    • covers projects.find_phase + projects.search_phases project scoping and deterministic ordering
    • covers projects.find_task + projects.search_tasks scoped lookups, filters, first/total/page metadata

Commands / runbook used

  • Targeted test run:
    • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

Gotchas discovered

  • Shared runtime package does not resolve @alga-psa/authorization/kernel in this test runtime; switched to local schema-aware read narrowing helper.
  • Test DB schema differs from legacy assumptions (project_number required; status column variants; user/client columns vary); test fixtures were made column-aware via information_schema checks.
  • Throwing standardized action objects inside try/catch requires pass-through handling; otherwise generic rethrow path can incorrectly map to INTERNAL_ERROR.

Implementation checkpoint — 2026-04-27 (Generic update actions)

Completed features

  • F017 + F018: Implemented projects.update schema+handler:

    • non-empty patch validation (project_name, description)
    • project update permission + project read auth check
    • changed-field diffing + no_op support
    • output includes updated project summary + changed metadata
    • workflow run audit writes include changed_fields and no_op
  • F019 + F020: Implemented projects.update_phase schema+handler:

    • non-empty patch validation (phase_name, description)
    • phase->project context validation
    • project update permission + read auth check
    • changed-field output + no_op + run audit
  • F021 + F022: Implemented projects.update_task schema+handler:

    • non-empty patch validation for task task_name and description
    • excludes move/status relocation fields by schema design
    • task->phase->project context validation
    • project update permission + read auth check
    • changed-field output + no_op + run audit

Completed tests

  • T006: Added DB-backed coverage for update happy paths and audit writes:

    • validates name/description updates for project, phase, and task
    • checks returned changed fields/no-op flags
    • asserts audit log operation records exist for all three update actions
  • T007: Added DB-backed validation/permission guard coverage:

    • empty patch rejects at schema parse time
    • missing project/phase/task returns NOT_FOUND
    • denied project:update returns PERMISSION_DENIED
    • verifies denied update does not mutate persisted project data

Checklist correction

  • T001 was initially marked complete during the first checkpoint but that was premature because it requires registration coverage for the full final project action surface. Reset to implemented:false until the full action set is present.

Implementation checkpoint — 2026-04-27 (Task move action)

Completed features

  • F023: Added projects.move_task input schema with:

    • task_id
    • target_phase_id
    • optional target_project_status_mapping_id
    • optional target_project_id
    • optional before_task_id / after_task_id (mutually exclusive)
    • picker metadata and dependency wiring for project/phase/task/status fields
  • F024 + F025: Implemented status remap/default resolution when explicit status mapping is omitted:

    • attempts same-project mapping reuse
    • attempts same underlying status id mapping in target project
    • falls back to first visible target project mapping by display order
    • cross-project path uses target-project mappings only
  • F026: Implemented move-time WBS/order metadata updates:

    • regenerates wbs_code based on target phase WBS + next ordinal
    • updates order_key when supported by schema
  • F027: Implemented project_ticket_links context rewrite on move:

    • updates project_id + phase_id for all links tied to moved task_id
  • F028: Added move output schema/result payload with previous/current project/phase/status mapping/status ids plus updated WBS/order/updated_at.

  • F029: Added permission checks, validation errors, and workflow run audit logging for projects.move_task.

Completed tests

  • T008: DB-backed same-project move test validates:

    • phase relocation
    • status mapping/status resolution non-null path
    • WBS/order metadata change
    • move audit record creation
  • T009: DB-backed cross-project move test validates:

    • target project/phase assignment
    • status mapping resolution in target project
    • project_ticket_links project/phase context updates
  • T010: DB-backed validation guard test validates:

    • missing task => NOT_FOUND
    • missing target phase => NOT_FOUND
    • invalid explicit target mapping => VALIDATION_ERROR

Gotchas discovered

  • Ticket fixture schema for this branch requires adaptive field population (e.g., client_id constraints and varying timestamp columns), so the test helper was made schema-aware via information_schema.
  • Same-project status remap behavior can legitimately keep source mapping in some fixture shapes; tests were adjusted to verify remap outcomes without over-constraining mapping identity.

Implementation checkpoint — 2026-04-27 (Task assignment action)

Completed features

  • F030: Added projects.assign_task input schema:

    • task_id
    • primary_user_id
    • additional_user_ids (array; deduped)
    • optional reason
    • no_op_if_already_assigned default true
    • optional idempotency_key
    • schema guard that rejects additional_user_ids containing the primary user
  • F031: Added assignment user resolution helper for active tenant users:

    • validates primary user exists and is active
    • validates all additional users exist and are active
    • enforces internal-user filtering where user schema supports user_type
    • returns deterministic deduped/sorted additional user ids
  • F032: Implemented no-op comparison for assignment requests:

    • compares current project_tasks.assigned_to
    • compares current task_resources.additional_user_id set
    • honors no_op_if_already_assigned (default true)
  • F033: Implemented assignment mutation and additional-user reconciliation:

    • updates project_tasks.assigned_to
    • clears assigned_team_id when present
    • replaces task_resources rows for the task with the resolved additional users
  • F034: Implemented assign action output shape:

    • task_id, assigned_to, additional_user_ids, no_op, updated_at
  • F035: Added permission checks and run audit logging for projects.assign_task:

    • requires project:update
    • validates project readability through task->phase->project context
    • writes workflow_action:projects.assign_task audit rows for both no-op and mutation paths

Completed tests

  • T011: Added DB-backed happy-path assignment test validating:

    • primary assignee change
    • additional-user reconciliation in task_resources
    • deterministic output
    • audit record creation
  • T012: Added DB-backed no-op test validating:

    • identical requested assignment returns no_op: true
    • task updated_at unchanged when no-op short-circuits
  • T013: Added DB-backed validation failure test validating:

    • inactive primary user rejected
    • missing additional user rejected
    • no partial mutation of project_tasks.assigned_to or existing task_resources

Commands/runbook used

  • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

Gotchas discovered

  • task_resources has no uniqueness constraint on (task_id, additional_user_id), so reconciliation uses delete+reinsert to keep assignment state canonical.
  • UUID lexical ordering is non-semantic; tests were adjusted to compare additional-user sets order-insensitively where needed.

Implementation checkpoint — 2026-04-27 (Task duplicate action)

Completed features

  • F036: Added projects.duplicate_task input schema with:

    • source_task_id
    • target_phase_id
    • optional target_project_status_mapping_id
    • copy toggles for primary assignee, additional assignees, checklist, and ticket links
  • F037: Implemented core duplicate behavior:

    • clones source task into target phase
    • appends (Copy) suffix to task name
    • preserves estimated hours when schema supports it
    • resets actual hours to 0 when schema supports it
    • resolves target status mapping/status similarly to move semantics
  • F038: Implemented optional checklist copy:

    • copies task_checklist_items rows to new task with new ids/timestamps
    • returns copied checklist count
  • F039: Implemented optional assignment copy:

    • optional primary assignment copy to project_tasks.assigned_to
    • optional additional assignment copy via task_resources
    • clears assigned_team_id on duplicates when that column exists
  • F040: Implemented optional ticket link copy with permission-aware filtering:

    • checks ticket:read permission before copying links
    • copies project_ticket_links into target project/phase/task context
    • writes matching ticket_entity_links records for duplicated task links
  • F041: Added duplicate action output schema:

    • source/new task ids
    • target project/phase/status mapping/status ids
    • copied relation counts
    • created_at
  • F042: Added permission checks and run audit logging:

    • requires project:create and project read checks for source/target context
    • writes workflow_action:projects.duplicate_task audit with target ids and copied counts

Completed tests

  • T014: DB-backed duplicate core behavior test validates:

    • new task creation in target phase/project
    • (Copy) suffix
    • description copy
    • estimated-hours preservation + actual-hours reset when columns exist
    • status mapping/status target metadata in output
  • T015: DB-backed optional relation copy test validates:

    • checklist copy count and persisted copied checklist rows
    • primary/additional assignment copy count and task_resources rows
    • ticket-link copy count and copied project_ticket_links target context

Commands/runbook used

  • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

Gotchas discovered

  • task_checklist_items/task_resources/project_tasks schemas vary slightly across migrations in this branch; test fixtures and assertions were kept column-aware to avoid false failures.
  • task_resources assignment rows are easiest to keep coherent via full-row clone with refreshed ids/timestamps and target task id, while overriding assigned_to according to copy options.

Implementation checkpoint — 2026-04-27 (Delete task/phase/project actions)

Completed features

  • F043: Added projects.delete_task input/output schemas with cleanup count fields:

    • input: task_id
    • output: task_id, deleted, deleted_ticket_link_count, deleted_checklist_item_count
  • F044: Implemented delete-task guard for associated project task time entries:

    • checks time_entries by work_item_type='project_task' and work_item_id=task_id
    • rejects with VALIDATION_ERROR when entries exist
  • F045: Implemented delete-task cleanup flow mirroring UI semantics:

    • deletes task ticket links
    • deletes task checklist items
    • removes task resources and task-side ticket entity links before deleting task row
  • F046: Added delete-task permission checks and audit logging:

    • requires project:delete
    • validates task->project context readability
    • writes workflow_action:projects.delete_task run audit
  • F047: Added projects.delete_phase input/output and handler:

    • input: phase_id
    • output: phase_id, project_id, deleted
    • deletes phase with project context validation
  • F048: Added delete-phase validation, permissions, standardized errors, and audit logging:

    • requires project:delete
    • validates phase + project context and readability
    • writes workflow_action:projects.delete_phase run audit
  • F049: Added projects.delete input/output schemas exposing validation/result fields:

    • input: project_id
    • output includes success, deleted, can_delete, code, message, dependencies, alternatives
  • F050: Implemented project delete handler with cleanup behavior:

    • collects phase/task descendants
    • blocks delete when descendant task time entries exist
    • cleans up project tags + task tags (tag_mappings)
    • cleans up project/task ticket links and project email reply tokens
    • removes descendant tasks/phases then project row
  • F051: Added delete-project permission checks, failure shape, and run audit logging:

    • requires project:delete
    • returns structured validation failure result when blocked
    • writes workflow_action:projects.delete run audit on mutation path

Completed tests

  • T016: DB-backed destructive task-delete happy path validates:

    • ticket-link + checklist cleanup counts
    • task row removed
    • related checklist/link rows removed
  • T017: DB-backed destructive guard test validates:

    • task delete rejected when project_task time entries exist
    • task/checklist/link rows remain intact
  • T018: DB-backed destructive phase/project delete coverage validates:

    • phase delete happy path
    • project delete happy path including cleanup of project/task tags, project ticket links, and email reply tokens
    • project delete validation-failure path when descendant task time entries exist

Commands/runbook used

  • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

Gotchas discovered

  • time_entries schema in this branch requires additional non-null columns (e.g., work_date, work_timezone), so destructive guard fixtures needed column-aware inserts.
  • Project delete cleanup needs to tolerate optional tables (email_reply_tokens, task_resources, ticket_entity_links) across schema variants; helper deletion logic was made table-existence-aware.

Completed features

  • F052: Added projects.link_ticket_to_task input schema:

    • task_id, ticket_id, optional project_id, optional phase_id, optional idempotency_key
    • picker metadata and dependency paths for task/project/phase context fields
  • F053: Added task/ticket/project/phase validation for link action:

    • validates task context exists
    • validates ticket exists
    • validates optional project/phase inputs match task context
  • F054: Implemented ensure/insert behavior for project_ticket_links:

    • idempotent existing-link detection
    • insert-on-miss with created vs existing signaling
  • F055: Implemented ensure/insert behavior for ticket_entity_links:

    • entity_type='project_task', entity_id=task_id, link_type='project_task'
    • metadata includes project/phase ids
    • created vs existing signaling
  • F056: Added link output schema with idempotency indicators and resolved ids:

    • project_ticket_link_id, ticket_entity_link_id
    • project_ticket_link_created, ticket_entity_link_created
  • F057: Added permission checks + run audit logging for link action:

    • requires project:update
    • writes workflow_action:projects.link_ticket_to_task
  • F058: Added shared project/project-task tag helper behavior mirroring contacts.add_tag:

    • tag normalization + dedupe
    • generated colors
    • tag definition upsert (conflict ignore)
    • mapping insert conflict ignore
    • returns added + existing
  • F059: Implemented projects.add_tag:

    • schema with project_id, tags[], optional idempotency_key
    • action-provided idempotency metadata
    • project validation + project:update permission
    • added/existing counts + audit logging
  • F060: Implemented projects.add_task_tag:

    • schema with task_id, tags[], optional idempotency_key
    • action-provided idempotency metadata
    • task/project context validation + project:update permission
    • added/existing counts + audit logging

Completed tests

  • T019: DB-backed link action happy path validates both link tables and project/phase metadata.
  • T020: DB-backed link idempotency test validates repeat calls do not duplicate effective links and report existing state.
  • T021: DB-backed project tag action test validates definition creation, idempotent mappings, and added/existing counts.
  • T022: DB-backed project-task tag action test validates definition creation, idempotent mappings, and added/existing counts.

Commands/runbook used

  • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts

Gotchas discovered

  • project_ticket_links/ticket_entity_links idempotency needed explicit existing-row fallback because unique constraints vary by schema/migration state.
  • Tag helper had to be column-aware for tag_definitions/tag_mappings to remain robust across branch schema variants.

Implementation checkpoint — 2026-04-27 (Picker resources + compatibility closeout)

Completed features

  • F061: Registered project fixed-picker resource support in workflow designer pickers:

    • added project to supported fixed-picker resources
    • picker now routes through fixed-value control path alongside existing resources
  • F062: Registered project-phase picker resource support with dependency handling:

    • added project-phase support
    • dependency hint defaults include project_id, target_project_id, and filters.project_id
  • F063: Registered project-task picker resource support with dependency handling:

    • added project-task support
    • dependency hint defaults include project + phase scoped paths (project_id, phase_id, target_project_id, target_phase_id, filter variants)
  • F064: Registered project-task-status picker support with dependency handling:

    • added project-task-status support
    • dependency hint defaults include project + phase scoped paths and filter variants
  • F065: Ensured dependency-aware picker metadata is honored end-to-end in fixed-value mode:

    • new project/phase/task/status kinds surface dependency-disabled explanations until upstream fixed values are provided
    • preserved no-tag-picker behavior (tags remain free-text arrays)
  • F066: Confirmed new project actions are present in workflow registry metadata with labels/descriptions/categories/schema/side-effect-idempotency metadata.

  • F067: Confirmed side-effectful project actions continue using standardized action errors, tenant-scoped mutations, and workflow run audit records.

  • F068: Verified projects.create_task compatibility remains intact after shared helper/picker expansion.

Completed tests

  • T001: Registry metadata test verifies new project actions are registered with expected flags/schemas/idempotency metadata.
  • T005: DB-backed authorization/tenant-scope test verifies project find/search/task search behavior is tenant scoped and authorization aware (with schema-aware branch handling when users.client_id is unavailable).
  • T023: Schema/picker metadata assertions validate project/phase/task/status picker metadata + dependency paths and absence of tag picker metadata.
  • T024: Workflow designer fixed-picker test validates project-related pickers render and dependency-disabled behavior is shown until upstream fixed values exist.
  • T025: Compatibility regression test verifies projects.create_task remains registered and functional.

Commands/runbook used

  • cd shared && npx vitest run workflow/runtime/actions/__tests__/registerProjectActionsMetadata.test.ts workflow/runtime/actions/__tests__/businessOperations.projects.db.test.ts
  • cd ee/server && npx vitest run src/components/workflow-designer/__tests__/InputMappingEditorPickerFields.test.tsx -t "T024"

Gotchas discovered

  • Project fixed pickers use async option loading in fallback CustomSelect mode (non-dedicated picker kinds), so immediate enabled assertions can be flaky; T024 now uses waitFor on project picker readiness.
  • Authorization fixture assumptions can differ across schema variants (users.client_id optional); T005 now guards for column presence to keep assertions meaningful in both variants.