Adds durable, identity-neutral, workflow-bound human tasks, and reconciles the REST surface with the approved Studio contract. - Flat summary/detail DTOs, a global capability descriptor, and workflow context captured at activation. - Scope is part of the list authorization predicate; manager decisions require manage:user-tasks; a denied command answers 404 so it cannot prove a task exists. - Guest sessions are task-scoped, action-allowlisted, and revoked when the task closes. Invitations resolve by token hash through the repository, wait in a Data Protection encrypted outbox, and are rate limited per caller. - Masked form values are disclosed only through an audited reveal command. - Store-specific concurrency failures are translated into a single UserTaskRevisionConflictException, so a concurrent edit returns the documented revision-conflict result behind any provider instead of a 500. EF Core (SQLite, SQL Server, PostgreSQL, MySQL, Oracle) and VNext persistence, hosted due/reconciliation/delivery workers, docs, and 49 tests. Note: this branch also carries two commits inherited from its branch point that are not part of User Tasks and are squashed in here — the revert-version allocation change from #7917 (WorkflowDefinitionPublisher.RevertVersionAsync now allocates from the last version rather than the latest) and an NU1903 package pin. Merged deliberately rather than rebased out.
16 KiB
Feature Specification: User Tasks
Feature Branch: 013-user-tasks
Created: 2026-08-17
Status: Approved for implementation
Input: Add an identity-neutral, durable human-task module to Elsa Core and a production-capable task workbench to Elsa Studio.
Product Intent
User Tasks let a workflow pause for a human decision, expose the work through a secure queue, and resume with a typed outcome. Elsa owns task durability and workflow coordination, while the host owns people, groups, authentication, and policy. No required dependency or foreign key may couple the module to Elsa.Identity.
User Scenarios & Testing
User Story 1 - Model and execute a user task (Priority: P1)
A workflow designer adds a User Task activity, configures visible context, assignment rules, completion actions, and an optional form. At runtime the workflow suspends until an authorized worker completes the task.
Why this priority: Durable workflow suspension and typed human completion are the feature's core value.
Independent Test: Publish and run a workflow containing one User Task, complete it through REST, and verify the selected action and normalized form data resume the correct activity exactly once.
Acceptance Scenarios:
- Given a task with a direct assignee, When the activity executes, Then an
Assignedtask and matching bookmark are persisted and only the assignee or a manager can read protected content. - Given a task with candidates, When an eligible worker claims it, Then the claim is atomic and competing claimants receive a conflict.
- Given a valid completion request, When it is accepted, Then the API returns
202, transitions throughCompleting, resumes the workflow, and eventually recordsCompletedonce.
User Story 2 - Work from a task inbox (Priority: P1)
Workers use Elsa Studio to see work assigned to them, discover available work, inspect authorized details, claim or release it, and complete it. Managers monitor all work and repair or reassign tasks that need attention.
Why this priority: A human task that cannot be found and acted upon is not operationally useful.
Independent Test: Seed tasks in each lifecycle state and verify each Studio tab, filter, action, disclosure rule, and mobile detail route against the REST API.
Acceptance Scenarios:
- Given an authenticated worker, When they open User Tasks, Then Assigned to me, Available, and History contain only relationship-authorized safe records.
- Given an unclaimed candidate task, When a candidate opens it, Then only its safe summary is visible until claim succeeds.
- Given a manager, When they open All or Needs Attention, Then they can diagnose, reassign, retry resolution, update due date or priority, and optionally cancel without completing on another person's behalf.
User Story 3 - Integrate a host-owned identity system (Priority: P1)
An Elsa integrator maps its authenticated principals and directory to opaque, tenant-scoped participant references and supplies task authorization without adopting Elsa Identity.
Why this priority: Identity neutrality is a hard compatibility boundary for embedded Elsa deployments.
Independent Test: Replace the default claims adapter and directory with test implementations and verify list visibility, live and snapshot groups, exclusions, management scope, and display resolution.
Acceptance Scenarios:
- Given a namespaced participant reference unknown to the directory, When an exact authenticated claim matches it, Then live-mode authorization can succeed and activation does not fail.
- Given snapshot membership that cannot be enumerated, When the task activates, Then it is created with a manager-only blocking health issue.
- Given an excluded participant, When they otherwise match assignment, Then access is denied unless the activity permits a manager override and an audited reason is supplied.
User Story 4 - Complete a task from an external portal (Priority: P2)
A workflow invites an external person. The host delivers a one-time token, the guest completes a pluggable verification challenge, and a task-scoped guest session permits completion without creating an Elsa user.
Why this priority: Customer approvals and document requests are high-value workflow scenarios.
Independent Test: Issue multiple invitations, verify one, confirm it atomically claims the task and revokes its siblings, and complete through the guest page with no identity-module dependency.
Acceptance Scenarios:
- Given an unverified invitation link, When it is opened, Then the response reveals generic content only.
- Given a valid challenge, When verification succeeds, Then a bounded task-scoped session is issued, the task is claimed, and competing invitations are revoked.
- Given an expired, consumed, or invalid token, When verification is attempted, Then the API returns a generic rate-limited failure without revealing task existence.
User Story 5 - Meet deadlines and recover reliably (Priority: P2)
Operators rely on due-date notifications, optional timeout outcomes, reconciliation, audit history, and provider-backed persistence across restarts and multiple nodes.
Why this priority: Human work is asynchronous and must survive failures and races.
Independent Test: Exercise completion-versus-timeout races on two nodes, restart between bookmark commit and projection, and verify first-writer-wins, idempotent notifications, and reconciliation.
Acceptance Scenarios:
- Given an overdue task without timeout enabled, When the scanner processes it repeatedly, Then one overdue notification is published and the task remains open.
- Given timeout enabled, When due time wins the terminal transition, Then the workflow resumes with reserved action
Timeoutand the task becomesTimedOut. - Given a committed bookmark lacking a task projection, When reconciliation runs, Then the missing task is recreated without duplication.
Edge Cases
- A task with neither assignee, candidates, nor invitations becomes
Unassigned, visible only to managers, and records a warning. - Release immediately revokes protected access and returns to
AvailableorUnassigned. - Reuse of an operation ID with the same payload is idempotent; reuse with different content conflicts.
- Completion, timeout, cancellation, and bookmark removal races use expected revision and a transitional state so exactly one terminal result wins.
- A configured form that cannot be resolved blocks worker completion and creates a manager-only health issue; repair retries the original reference.
- “Latest” form references are pinned at activation so later form changes cannot alter an open task.
- Search never examines protected instructions, task data, form data, or completion data.
- Terminal history grants protected data only to the completer and managers; prior assignees receive summary and audit only.
- Unknown display names degrade to opaque participant IDs and a health indication, not workflow failure.
- Invitations expire at the configured bound, defaulting to the earlier of seven days or task due time.
Requirements
Functional Requirements
- FR-001: The system MUST provide a blocking
UserTaskworkflow activity with expression-capable design inputs and typed result outputs. - FR-002: The activity MUST materialize title, summary, reference, tags, task type, requester, priority, due time, instructions, bounded task data, assignment rules, exclusions, actions, form reference, and invitation definitions at activation.
- FR-003: Completion action keys MUST be immutable literals;
TimeoutandCancelledare reserved; labels MAY be expressions. - FR-004: Task status MUST follow
Unassigned,Available,Assigned, transitionalCompleting/TimingOut/Cancelling, and terminalCompleted/TimedOut/Cancelledstates. - FR-005: Claim, release, direct assign, reassign, due-date update, priority update, completion, timeout, and optional cancellation MUST be atomic, revision-checked, idempotent where applicable, and audited.
- FR-006: Completion MUST be asynchronous: accept a command, persist operation state, enqueue bookmark resumption, and finalize from bookmark removal or reconciliation.
- FR-007: The module MUST project committed User Task bookmarks and reconcile missing projections, stale operations, and orphan task records in bounded pages.
- FR-008: The module MUST use opaque participant references composed of tenant, provider namespace, participant type, and external ID, with no required Elsa Identity dependency.
- FR-009: The host MUST be able to replace principal mapping, identity resolution, participant lookup, task policy, form, invitation delivery, invitation verification, and guest session services.
- FR-010: Authorization MUST require both operation permission and a task relationship; managers remain tenant-scoped and guest sessions remain task-scoped.
- FR-011: Candidate users MUST see safe summary only; protected instructions, task data, and forms MUST be disclosed only after assignment and revoked on release.
- FR-012: Live group membership MUST use current principal claims; snapshot membership MUST expand at activation and create a manager-only issue if enumeration fails.
- FR-013: Explicit exclusions MUST override ordinary assignment; an optional manager override MUST require a reason and audit event.
- FR-014: The requester MUST be searchable and visible but MUST confer no access.
- FR-015: Forms MUST be provider-neutral, version-pinned at activation, validated and normalized by the provider, and repairable only by retrying the original reference.
- FR-016: Tasks without a form MUST accept only a configured action and no arbitrary completion JSON.
- FR-017: Due processing MUST publish one idempotent overdue notification; when timeout is enabled it MUST resume with
Timeoutand finalize asTimedOut. - FR-018: Manager cancellation MUST be activity-enabled, permission-protected, reason-required, and resume with
Cancelled. - FR-019: REST APIs under
/user-tasksMUST provide cursor-based authorized search, details, events, capabilities, claim/release/assign, priority/due update, complete/cancel, resolution retry, participant lookup, and invitation operations. - FR-020: Terminal mutation APIs MUST require
expectedRevisionandoperationId, return202when accepted,409for races or divergent operation reuse, and422for domain validation. - FR-021: Anonymous invitation APIs MUST use generic errors and rate limiting; raw invitation secrets MUST never be stored outside an encrypted transient delivery outbox and MUST be returned only once to a dispatcher.
- FR-022: The first successfully verified invitation MUST atomically claim the task, revoke sibling invitations, and issue a bounded guest session; guests MUST NOT release tasks.
- FR-023: Persistence MUST offer an in-memory default plus EF Core packages for SQLite, SQL Server, PostgreSQL, MySQL, and Oracle, and a VNext provider abstraction with equivalent behavior.
- FR-024: Storage MUST index normalized query fields and use bounded JSON only for protected task, form, and completion payloads; default payload limit is 256 KiB.
- FR-025: Audit events MUST be append-only, tenant-scoped, and exclude raw protected payloads.
- FR-026: Terminal retention MUST be indefinite by default with configurable purge that never deletes open tasks.
- FR-027: Studio MUST add Workflows → User Tasks with Assigned to me, Available, History, and manager-only All and Needs Attention views.
- FR-028: Studio MUST provide desktop queue/detail and mobile detail routes, URL-backed filters, workflow-instance deep links, capability-driven actions, safe disclosure, and no embedded diagram.
- FR-029: Studio MUST consume metadata-free SignalR invalidations and requery, with polling fallback.
- FR-030: Studio's generic activity editor MUST support all activity fields; participant lookup is optional with raw namespaced references and expressions as fallback.
- FR-031: Studio MUST ship a replaceable guest task page that reveals no task-specific information before successful verification.
- FR-032: Search MUST be limited to safe title, summary, reference, tags, task type, workflow, and correlation fields and MUST use opaque cursor pagination with stable sorting and optional totals.
- FR-033: Existing generic
RunTaskbehavior and/tasks/{id}/completeMUST remain unchanged. - FR-034: The system MUST publish mediator lifecycle notifications and metadata-free realtime invalidations for external integrations.
Key Entities
- UserTask: Durable runtime projection linked to tenant, workflow, activity instance, and bookmark; owns lifecycle, safe/protected content, timing, assignment, form pin, health, revision, and terminal result.
- ParticipantReference: Opaque tenant/provider/type/ID identity value with an optional display snapshot.
- Candidate / Exclusion / SnapshotMember: Assignment relationships materialized for authorization and querying.
- UserTaskAction: Stable completion key and materialized display label.
- UserTaskOperation: Idempotency and asynchronous transition record keyed by task and operation ID.
- UserTaskEvent: Append-only audit event with actor reference and safe metadata.
- UserTaskInvitation: Hashed one-time invitation, challenge policy, expiry, state, and guest claim relationship.
- GuestSession: Revocable, task-scoped capabilities and expiry.
- InvitationDelivery: Encrypted transient outbox entry for retryable host delivery.
Success Criteria
- SC-001: A published workflow can suspend at a User Task and resume exactly once with a typed result across restart and retry tests.
- SC-002: Two concurrent claimants or terminal actors yield exactly one winner in all automated race tests.
- SC-003: Authorization tests prove no candidate, released worker, unrelated user, cross-tenant manager, or unverified guest can read protected content.
- SC-004: Authorized list queries remain cursor-stable at the target envelope of 100,000 open tasks and millions of terminal tasks per tenant without loading protected payloads.
- SC-005: All supported persistence providers expose the same schema contract and the SQLite integration suite proves restart durability and indexed query behavior.
- SC-006: A worker can locate, claim, and complete a seeded task from Studio on desktop and a narrow viewport without entering an Elsa Identity user ID.
- SC-007: Reconciliation restores every intentionally interrupted bookmark/task/operation test fixture without duplicate tasks or workflow resumes.
- SC-008: Requirements, contracts, implementation tasks, automated tests, and documentation retain stable traceability identifiers.
Assumptions and Scope
- Hosts already provide authentication and a permission pipeline; the module provides replaceable adapters and safe defaults, not a user database.
- Server time is UTC and persistence providers support optimistic concurrency.
- External files are referenced by URI or provider ID; they are not embedded in task JSON.
- V1 excludes standalone tasks, a native form builder, drafts, comments, attachments, formal delegation, bulk actions, named saved filters, and multi-response tasks.
- GitHub issue, project, pull-request, and remote review gates are intentionally skipped for this delivery because of reported GitHub downtime; all local specification, test, build, and self-review gates remain in force.