#8174·elsa-core

EF UserTasks History scope omits CompletedBy (InMemory/VNext OR events)

Author: sfmskywalkerCreated Sep 15, 2026Updated Sep 15, 2026
Labelsbugcorepersistenceprio lowelsa 3testtriaged

Problem

UserTaskQueryScopeKind.History visibility diverges across providers. InMemory and VNext treat a terminal task as History-visible when the subject is CompletedBy OR appears as an event actor. EF only correlates UserTaskEvents actor columns and never consults CompletedBy / CompletionActorJson.

That breaks parity with the access policy’s completer rule (DefaultUserTaskAccessPolicy authorizes ReadSummary via isCompleter || acted) and can hide a completer’s terminal task from History on EF hosts when CompletedBy is set but no matching event actor row exists (incomplete event write, import/recon path, or actor-null system events).

Evidence

  • InMemory: src/modules/Elsa.UserTasks/Repositories/InMemoryUserTaskRepository.csIsVisible History branch: CompletedBy?.Matches(subject) || Events.Any(actor.Matches)
  • VNext: src/modules/Elsa.UserTasks.Persistence.VNext/Repositories/VNextUserTaskRepository.cs — same OR
  • EF: src/modules/Elsa.UserTasks.Persistence.EFCore/Repositories/EFCoreUserTaskRepository.csBuildQuery History branch filters terminal status + UserTaskEvents ActorProvider/Type/Id only
  • EF stores completer as UserTaskRecord.CompletionActorJson (PersistenceModels.cs) and maps it to UserTask.CompletedBy on load, but History never predicates on it
  • Manager Complete/Cancel normally write both CompletedBy and an actor event (DefaultUserTaskManager), so the gap is often masked — still a store-contract divergence
  • Conformance (UserTaskRepositoryConformanceTests) has no History / CompletedBy visibility case

Proposal

  1. Align EF History with InMemory/VNext: terminal + (CompletedBy matches subject OR event actor matches). Prefer denormalized completer provider/type/id columns (mirror Assignee*) if JSON predicates are undesirable; smallest correct query wins.
  2. Add conformance: project a terminal task with CompletedBy = alice and no matching event actor; History for alice returns the task on InMemory, Sqlite EF, and VNext. Optionally assert event-actor-only path still works.

Notes for triage

Milestone left unset for Elsa 3 Issue Triage / Elsa 3 Crew Lead. Soft proposal: 3.9 Backlog — Triage/Crew Lead decide (UserTasks query parity leftover after #8109/#8110/#8111).

Source: elsa-workflows/elsa-core