Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ private async Task<ActivityDescriptor> CreateAgentActivityDescriptor(AgentConfig

activityDescriptor.Constructor = context =>
{
var activity = context.CreateActivity<AgentActivity>();
var result = context.CreateActivity<AgentActivity>();
var activity = result.Activity;
activity.Type = activityTypeName;
activity.AgentName = agentConfig.Name;
activity.RunAsynchronously = true;
return activity;
return result;
};

activityDescriptor.Inputs.Clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ private async Task<ActivityDescriptor> CreateActivityDescriptorAsync(string cont
activityDescriptor.Description = description;
activityDescriptor.Constructor = context =>
{
var activity = context.CreateActivity<ContentItemEvent>();
var result = context.CreateActivity<ContentItemEvent>();
var activity = result.Activity;
activity.Type = fullTypeName;
activity.ContentType = contentType;
activity.EventType = eventType;
return activity;
return result;
};

foreach (var inputDescriptor in activityDescriptor.Inputs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ private async Task<ActivityDescriptor> CreateActivityDescriptorAsync(WebhookSour
activityDescriptor.Description = eventTypeDescription;
activityDescriptor.Constructor = context =>
{
var activity = context.CreateActivity<WebhookEventReceived>();
var result = context.CreateActivity<WebhookEventReceived>();
var activity = result.Activity;
activity.Type = fullTypeName;
activity.EventType = eventType.EventType;
activity.PayloadType = eventType.PayloadType;
return activity;
return result;
};

var eventTypeDescriptor = activityDescriptor.Inputs.First(x => x.Name == nameof(WebhookEventReceived.EventType));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Diagnostics.CodeAnalysis;
using FluentMigrator;
using JetBrains.Annotations;
using static System.Int32;

namespace Elsa.Persistence.Dapper.Migrations.Runtime;

/// <inheritdoc />
[Migration(20007, "Elsa:Runtime:V3.7")]
[PublicAPI]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class V3_7 : Migration
{
/// <inheritdoc />
public override void Up()
{
Alter.Table("ActivityExecutionRecords").AddColumn("SerializedMetadata").AsString(MaxValue).Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("SchedulingActivityExecutionId").AsString().Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("SchedulingActivityId").AsString().Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("SchedulingWorkflowInstanceId").AsString().Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("CallStackDepth").AsInt32().Nullable();
Alter.Table("ActivityExecutionRecords").AddColumn("AggregateFaultCount").AsInt32().NotNullable().WithDefaultValue(0);
}
Comment on lines 15 to 23
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration is missing the AggregateFaultCount column. According to the changes in ActivityExecutionRecordRecord.cs, there is an AggregateFaultCount field being added to the entity (lines 88-89), and this field is being mapped in the store implementation (lines 189 and 219 in DapperActivityExecutionRecordStore.cs). However, the migration Up() method does not add this column to the database table. The Down() method should also include removal of this column for consistency.

Copilot uses AI. Check for mistakes.

/// <inheritdoc />
public override void Down()
{
Delete.Column("SerializedMetadata").FromTable("ActivityExecutionRecords");
Delete.Column("SchedulingActivityExecutionId").FromTable("ActivityExecutionRecords");
Delete.Column("SchedulingActivityId").FromTable("ActivityExecutionRecords");
Delete.Column("SchedulingWorkflowInstanceId").FromTable("ActivityExecutionRecords");
Delete.Column("CallStackDepth").FromTable("ActivityExecutionRecords");
Delete.Column("AggregateFaultCount").FromTable("ActivityExecutionRecords");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ internal class ActivityExecutionRecordRecord : Record
/// </summary>
public string? SerializedProperties { get; set; }

/// <summary>
/// Lightweight metadata associated with the activity execution.
/// </summary>
public string? SerializedMetadata { get; set; }

/// <summary>
/// Gets or sets the time at which the activity execution began.
/// </summary>
Expand All @@ -76,9 +81,34 @@ internal class ActivityExecutionRecordRecord : Record
/// Gets or sets the status of the activity.
/// </summary>
public string Status { get; set; } = null!;


/// <summary>
/// Gets or sets the aggregated count of faults encountered during the execution of the activity instance and its descendants.
/// </summary>
public int AggregateFaultCount { get; set; }

/// <summary>
/// Gets or sets the time at which the activity execution completed.
/// </summary>
public DateTimeOffset? CompletedAt { get; set; }
}

/// <summary>
/// The ID of the activity execution context that scheduled this activity execution.
/// </summary>
public string? SchedulingActivityExecutionId { get; set; }

/// <summary>
/// The ID of the activity that scheduled this activity execution (denormalized).
/// </summary>
public string? SchedulingActivityId { get; set; }

/// <summary>
/// The workflow instance ID of the workflow that scheduled this activity execution.
/// </summary>
public string? SchedulingWorkflowInstanceId { get; set; }

/// <summary>
/// The depth of this activity in the call stack (0 for root activities).
/// </summary>
public int? CallStackDepth { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,19 @@ internal class ActivityExecutionSummaryRecord : Record
/// Gets or sets the status of the activity.
/// </summary>
public string Status { get; set; } = null!;

/// <summary>
/// Lightweight metadata associated with the activity execution.
/// </summary>
public string? SerializedMetadata { get; set; }

/// <summary>
/// Gets or sets the aggregated count of faults encountered during the execution of the activity instance and its descendants.
/// </summary>
public int AggregateFaultCount { get; set; }

/// <summary>
/// Gets or sets the time at which the activity execution completed.
/// </summary>
public DateTimeOffset? CompletedAt { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Elsa.Common.Models;
using Elsa.Persistence.Dapper.Extensions;
using Elsa.Persistence.Dapper.Models;
using Elsa.Persistence.Dapper.Modules.Runtime.Records;
Expand Down Expand Up @@ -88,15 +89,29 @@ public async Task<long> DeleteManyAsync(ActivityExecutionRecordFilter filter, Ca
return await store.DeleteAsync(q => ApplyFilter(q, filter), cancellationToken);
}


private static void ApplyFilter(ParameterizedQuery query, ActivityExecutionRecordFilter filter)
{
query
.Is(nameof(ActivityExecutionRecordRecord.Id), filter.Id)
.In(nameof(ActivityExecutionRecordRecord.Id), filter.Ids)
.Is(nameof(ActivityExecutionRecordRecord.ActivityId), filter.ActivityId)
.In(nameof(ActivityExecutionRecordRecord.ActivityId), filter.ActivityIds)
.Is(nameof(ActivityExecutionRecordRecord.ActivityNodeId), filter.ActivityNodeId)
.In(nameof(ActivityExecutionRecordRecord.ActivityNodeId), filter.ActivityNodeIds)
.Is(nameof(ActivityExecutionRecordRecord.ActivityName), filter.Name)
.In(nameof(ActivityExecutionRecordRecord.ActivityName), filter.Names)
.Is(nameof(ActivityExecutionRecordRecord.Status), filter.Status?.ToString())
.In(nameof(ActivityExecutionRecordRecord.Status), filter.Statuses?.Select(x => x.ToString()))
.Is(nameof(ActivityExecutionRecordRecord.WorkflowInstanceId), filter.WorkflowInstanceId)
.In(nameof(ActivityExecutionRecordRecord.WorkflowInstanceId), filter.WorkflowInstanceIds);
.In(nameof(ActivityExecutionRecordRecord.WorkflowInstanceId), filter.WorkflowInstanceIds)
.Is(nameof(ActivityExecutionRecordRecord.SchedulingActivityExecutionId), filter.SchedulingActivityExecutionId)
.In(nameof(ActivityExecutionRecordRecord.SchedulingActivityExecutionId), filter.SchedulingActivityExecutionIds)
.Is(nameof(ActivityExecutionRecordRecord.SchedulingActivityId), filter.SchedulingActivityId)
.In(nameof(ActivityExecutionRecordRecord.SchedulingActivityId), filter.SchedulingActivityIds)
.Is(nameof(ActivityExecutionRecordRecord.SchedulingWorkflowInstanceId), filter.SchedulingWorkflowInstanceId)
.In(nameof(ActivityExecutionRecordRecord.SchedulingWorkflowInstanceId), filter.SchedulingWorkflowInstanceIds)
.Is(nameof(ActivityExecutionRecordRecord.CallStackDepth), filter.CallStackDepth);

if (filter.Completed != null)
{
Comment on lines 115 to 117
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for stopping at workflow boundaries appears to be incorrect. The current condition stops traversal whenever SchedulingWorkflowInstanceId is not null, even if it's the same workflow instance. This would incorrectly stop the traversal within the same workflow when one activity schedules another. The condition should compare SchedulingWorkflowInstanceId with WorkflowInstanceId to detect actual cross-workflow boundaries. Consider changing the condition to: if (!includeCrossWorkflowChain && mappedRecord.SchedulingWorkflowInstanceId != null && mappedRecord.SchedulingWorkflowInstanceId != mappedRecord.WorkflowInstanceId)

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -127,6 +142,12 @@ private ActivityExecutionRecordRecord Map(ActivityExecutionRecord source)
SerializedOutputs = source.Outputs?.Any() == true ? safeSerializer.Serialize(source.Outputs) : null,
SerializedException = source.Exception != null ? payloadSerializer.Serialize(source.Exception) : null,
SerializedProperties = source.Properties?.Any() == true ? safeSerializer.Serialize(source.Properties) : null,
SerializedMetadata = source.Metadata?.Any() == true ? payloadSerializer.Serialize(source.Metadata) : null,
AggregateFaultCount = source.AggregateFaultCount,
SchedulingActivityExecutionId = source.SchedulingActivityExecutionId,
SchedulingActivityId = source.SchedulingActivityId,
SchedulingWorkflowInstanceId = source.SchedulingWorkflowInstanceId,
CallStackDepth = source.CallStackDepth,
TenantId = source.TenantId
};
}
Expand All @@ -151,6 +172,12 @@ private ActivityExecutionRecord Map(ActivityExecutionRecordRecord source)
Outputs = source.SerializedOutputs != null ? safeSerializer.Deserialize<IDictionary<string, object?>>(source.SerializedOutputs) : null,
Exception = source.SerializedException != null ? payloadSerializer.Deserialize<ExceptionState>(source.SerializedException) : null,
Properties = source.SerializedProperties != null ? safeSerializer.Deserialize<IDictionary<string, object>>(source.SerializedProperties) : null,
Metadata = source.SerializedMetadata != null ? payloadSerializer.Deserialize<IDictionary<string, object>>(source.SerializedMetadata) : null,
AggregateFaultCount = source.AggregateFaultCount,
SchedulingActivityExecutionId = source.SchedulingActivityExecutionId,
SchedulingActivityId = source.SchedulingActivityId,
SchedulingWorkflowInstanceId = source.SchedulingWorkflowInstanceId,
CallStackDepth = source.CallStackDepth,
TenantId = source.TenantId
};
}
Expand All @@ -170,7 +197,9 @@ private ActivityExecutionRecordSummary MapSummary(ActivityExecutionSummaryRecord
HasBookmarks = source.HasBookmarks,
Status = Enum.Parse<ActivityStatus>(source.Status),
ActivityTypeVersion = source.ActivityTypeVersion,
AggregateFaultCount = source.AggregateFaultCount,
Metadata = source.SerializedMetadata != null ? payloadSerializer.Deserialize<IDictionary<string, object>>(source.SerializedMetadata) : null,
TenantId = source.TenantId
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public Task<long> DeleteManyAsync(ActivityExecutionRecordFilter filter, Cancella
return mongoDbStore.DeleteWhereAsync<string>(queryable => Filter(queryable, filter), x => x.Id, cancellationToken);
}


private IQueryable<ActivityExecutionRecord> Filter(IQueryable<ActivityExecutionRecord> queryable, ActivityExecutionRecordFilter filter)
{
return filter.Apply(queryable);
Expand All @@ -89,4 +90,4 @@ private IQueryable<ActivityExecutionRecord> Paginate(IQueryable<ActivityExecutio
{
return queryable.Paginate(pageArgs);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ private async Task<ActivityDescriptor> CreateMessageReceivedDescriptor(Type mess
},
Constructor = context =>
{
var activity = context.CreateActivity<MessageReceived>();
var result = context.CreateActivity<MessageReceived>();
var activity = result.Activity;
activity.Type = fullTypeName;
activity.MessageType = messageType;
return activity;
return result;
}
};
}
Expand Down Expand Up @@ -117,10 +118,11 @@ private async Task<ActivityDescriptor> CreatePublishMessageDescriptor(Type messa
},
Constructor = context =>
{
var activity = context.CreateActivity<PublishMessage>();
var result = context.CreateActivity<PublishMessage>();
var activity = result.Activity;
activity.Type = fullTypeName;
activity.MessageType = messageType;
return activity;
return result;
}
};
}
Expand Down
Loading