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
647 lines
35 KiB
Markdown
647 lines
35 KiB
Markdown
# 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.
|
|
|
|
## Ticket-to-task link decision
|
|
|
|
- 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.
|
|
|
|
## Implementation checkpoint — 2026-04-27 (Ticket-link + tag actions)
|
|
|
|
### 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.
|