Add project files.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# 1. Record architecture decisions
|
||||
|
||||
Date: 2025-04-01
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
We need to record the architectural decisions made on this project.
|
||||
|
||||
## Decision
|
||||
|
||||
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
||||
|
||||
## Consequences
|
||||
|
||||
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
|
||||
@@ -0,0 +1,23 @@
|
||||
# 2. Fault Propagation from Child to Parent Activities
|
||||
|
||||
Date: 2025-04-01
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
When an activity throws an exception during execution, the `ExceptionHandlingMiddleware` is responsible for handling the fault. In addition to logging the exception and recording an incident, it also explicitly faults all ancestor activities using the `GetAncestors()` method and calling `TransitionTo(ActivityStatus.Faulted)` on each one.
|
||||
|
||||
At the time of implementing this behavior, the rationale was likely to bubble up the fault status to allow Elsa Studio to display that a given activity has one or more faulting descendant activities.
|
||||
|
||||
However, this behavior causes unintended side effects when the faulted activity is later resumed and completes successfully. Since the parent (e.g., a `Flowchart`) was faulted earlier, it remains in a `Faulted` state, unaware that the child it depends on has now succeeded. As a result, the Flowchart itself never reaches `Completed`, and the entire workflow instance is stuck in `Faulted` or `Running`, depending on the selected fault strategy.
|
||||
|
||||
## Decision
|
||||
A new field will be introduced that represents an aggregate count of the faults occurring in descendant activities.
|
||||
|
||||
## Consequences
|
||||
- Allows composite activities to reflect a failed state immediately and consistently when one of their children fails.
|
||||
|
||||
## Alternatives Considered
|
||||
- Query descendants at runtime to get an aggregate fault count instead. However, this is an expensive operation that can be time consuming.
|
||||
@@ -0,0 +1,40 @@
|
||||
# 3. Direct Bookmark Management in WorkflowExecutionContext
|
||||
|
||||
Date: 2025-04-01
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
Currently, `ActivityExecutionContext` maintains a temporary list of bookmarks created by the activity during its execution. After the activity completes, a middleware is responsible for copying these bookmarks into the `WorkflowExecutionContext.Bookmarks` list, which is then persisted to the database.
|
||||
|
||||
This architecture leads to a subtle issue upon resumption:
|
||||
- When a workflow is resumed from a persisted state, only the `WorkflowExecutionContext` and its `Bookmarks` are restored from the database.
|
||||
- The `ActivityExecutionContext`'s temporary `Bookmarks` list remains empty.
|
||||
- As a result, if the resumed activity attempts to cancel (or modify) its previously created bookmarks, it cannot, because it has no access to them—changes are not tracked.
|
||||
|
||||
However, there is an existing convention that determines that if an activity created a bookmark during its execution, the activity will not automatically complete.
|
||||
Specifically, this is implemented in the `AutoCompleteBehavior`, which is installed by the `CodeActivity` and possibly by custom activities.
|
||||
To keep this behavior in tact, we will still maintain a private list of bookmarks, one that is explicitly purposed for maintaining *new** bookmarks, temporarily for the lifetime of the ActivityExecutionContext in memory.
|
||||
|
||||
## Decision
|
||||
Eliminate the temporary `Bookmarks` list in `ActivityExecutionContext`. Instead, have activities add bookmarks directly to the `WorkflowExecutionContext.Bookmarks` list.
|
||||
|
||||
This is possible and safe because each `Bookmark` already contains:
|
||||
- A reference to the originating `ActivityId`
|
||||
- A reference to the originating `ActivityInstanceId`
|
||||
|
||||
These references allow accurate tracking and filtering of bookmarks, even when multiple activities are active.
|
||||
|
||||
Additionally, maintain a new temporary bookmarks list that explicitly stores newly created bookmarks. This list does not have to be copied into the WorkflowExecutionContext's Bookmarks property, given that this property will now be updated directly.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Simplifies the architecture by removing the need for middleware to copy bookmarks post-execution.
|
||||
- Fixes a bug where resumed activities could not cancel their previously created bookmarks.
|
||||
- Reduces the chance of inconsistencies between temporary and persisted bookmark state.
|
||||
- Ensures bookmark changes are always applied to the persisted, canonical source of truth.
|
||||
|
||||
## Alternatives Considered
|
||||
- Keep the existing temporary list and enhance the system to hydrate the `ActivityExecutionContext.Bookmarks` list on resume. Rejected due to added complexity and duplication of state.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 4. Activity Execution Snapshots
|
||||
|
||||
**Date:** 2025-08-04
|
||||
**Status:** Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Today the `ActivityExecutionContext` is persisted only at *commit points*.
|
||||
If an activity references a workflow variable that changes after it has run—but before the next commit—the value saved is the *later* value, not the one that existed when the activity executed.
|
||||
As a result, the Workflow Instance Viewer shows misleading data: users expect to see the variable values *at execution time*, not at commit time.
|
||||
|
||||
## Decision
|
||||
|
||||
Capture a **snapshot** of the `ActivityExecutionContext` immediately when an activity executes.
|
||||
The snapshot must include:
|
||||
|
||||
* All workflow variables and their values at that moment.
|
||||
* Any other execution-specific metadata required for replay or inspection.
|
||||
|
||||
The snapshot is created by serializing the `ActivityExecutionRecord` to JSON and storing it in the database.
|
||||
The persistence layer will be updated to handle this new snapshot field, ensuring it is stored alongside the activity execution record.
|
||||
|
||||
## Consequences
|
||||
|
||||
* The Workflow Instance Viewer will now display the exact state that the activity saw, eliminating confusion during debugging and auditing.
|
||||
* Additional storage will be consumed for each snapshot. We accept this overhead in exchange for correctness and developer experience.
|
||||
* Existing persistence schemas will require a non-breaking migration to store the snapshot payload.
|
||||
* Workflow instances before this change will not have snapshots, but they will still be replayable even if the variable values are not accurate at execution time.
|
||||
@@ -0,0 +1,23 @@
|
||||
# 5. Tenant Deleted Event
|
||||
|
||||
Date: 2025-08-05
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
As outlined in issue [#6661](https://github.com/elsa-workflows/elsa-core/issues/6661), there is a need to differentiate between **Tenant Deactivating** and **Tenant Deleting** events.
|
||||
|
||||
Currently, the `TenantDeactivated` event is used to unregister timer-based triggers. However, this causes the triggers to be unregistered when the application host shuts down, which is not the desired behavior. Instead, we want the triggers to remain registered until the tenant is explicitly deleted.
|
||||
|
||||
## Decision
|
||||
|
||||
To address this, we will introduce a new event called `TenantDeleted`. This event will be raised when a tenant is deleted and will be responsible for unregistering timer-based triggers. This ensures that the triggers remain active until the tenant is explicitly deleted.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Timer-based triggers will no longer be unregistered during tenant deactivation. Instead, they will remain active until the tenant is deleted.
|
||||
- The `TenantDeactivated` event will continue to be used for deactivating tenants without affecting the registration of timer-based triggers.
|
||||
- The new `TenantDeleted` event will specifically handle the cleanup of resources associated with a tenant when it is deleted, ensuring a clear separation of responsibilities.
|
||||
@@ -0,0 +1,14 @@
|
||||
digraph {
|
||||
node [shape=plaintext];
|
||||
subgraph {
|
||||
_1 [label="1. Record architecture decisions"; URL="0001-record-architecture-decisions.html"];
|
||||
_2 [label="2. Fault Propagation from Child to Parent Activities"; URL="0002-fault-propagation-from-child-to-parent-activities.html"];
|
||||
_1 -> _2 [style="dotted", weight=1];
|
||||
_3 [label="3. Direct Bookmark Management in WorkflowExecutionContext"; URL="0003-direct-bookmark-management-in-workflowexecutioncontext.html"];
|
||||
_2 -> _3 [style="dotted", weight=1];
|
||||
_4 [label="4. Activity Execution Snapshots"; URL="0004-activity-execution-snapshots.html"];
|
||||
_3 -> _4 [style="dotted", weight=1];
|
||||
_5 [label="5. Tenant Deleted Event"; URL="0005-tenant-deleted-event.html"];
|
||||
_4 -> _5 [style="dotted", weight=1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
* [1. Record architecture decisions](0001-record-architecture-decisions.md)
|
||||
* [2. Fault Propagation from Child to Parent Activities](0002-fault-propagation-from-child-to-parent-activities.md)
|
||||
* [3. Direct Bookmark Management in WorkflowExecutionContext](0003-direct-bookmark-management-in-workflowexecutioncontext.md)
|
||||
* [4. Activity Execution Snapshots](0004-activity-execution-snapshots.md)
|
||||
* [5. Tenant Deleted Event](0005-tenant-deleted-event.md)
|
||||
@@ -0,0 +1,17 @@
|
||||
## Bounty Candidacy Announcement
|
||||
|
||||
I am announcing my intention to tackle this bounty issue. Below is my acknowledgment of the key eligibility requirements and rules of the Elsa Workflows Bounty Hunting Program:
|
||||
|
||||
- [ ] I have a GitHub account set up for receiving payments via GitHub Sponsors.
|
||||
- [ ] I understand this issue is assigned on a "First Come, First Served" basis.
|
||||
- [ ] I am aware of the specific time limit for this issue and commit to submitting my solution within this timeframe.
|
||||
- [ ] I understand that I must first describe my proposed solution in a comment on this issue or via a discussion linked to this bounty, before starting my work.
|
||||
- [ ] I have reviewed the [Bounty Hunting Program Guidelines](#5211) in detail.
|
||||
|
||||
**GitHub Username**: `YourGitHubUsernameHere`
|
||||
|
||||
**Estimated Start Date**: `YYYY-MM-DD`
|
||||
|
||||
**Notes/Comments**: `Any additional comments or notes you wish to add`
|
||||
|
||||
By checking the boxes above, I signify my understanding and acceptance of the program rules and confirm my intent to proceed with resolving this issue as per the guidelines of the Elsa Workflows Bounty Hunting Program.
|
||||
@@ -0,0 +1,35 @@
|
||||
⭐ **This Issue is Part of the Elsa Workflows Bounty Hunting Program** ⭐
|
||||
|
||||
Are you ready to contribute and earn rewards for your efforts?
|
||||
This issue offers a bounty for the first eligible contributor who successfully resolves it within the designated time limit, according to our comprehensive program rules.
|
||||
|
||||
**Before You Start**:
|
||||
- Ensure you have a GitHub account set up to receive payments via GitHub Sponsors.
|
||||
- Publicly announce your intention to tackle this issue by commenting below. This follows our "First Come, First Served" rule.
|
||||
- Be aware of the specific time limit for this issue, as failing to submit a solution within this timeframe may open the opportunity for others.
|
||||
|
||||
📜 **For a detailed overview of participation, submission guidelines, and the process for claiming rewards, please visit our [Bounty Hunting Program Guidelines](https://github.com/elsa-workflows/elsa-core/issues/5211).**
|
||||
|
||||
To announce your intention to claim this bounty, please comment below. You can use this template:
|
||||
|
||||
\`\`\`markdown
|
||||
## Bounty Candidacy Announcement
|
||||
|
||||
I am announcing my intention to tackle this bounty issue. Below is my acknowledgment of the key eligibility requirements and rules of the Elsa Workflows Bounty Hunting Program:
|
||||
|
||||
- [ ] I have a GitHub account set up for receiving payments via GitHub Sponsors.
|
||||
- [ ] I understand this issue is assigned on a "First Come, First Served" basis.
|
||||
- [ ] I am aware of the specific time limit for this issue and commit to submitting my solution within this timeframe.
|
||||
- [ ] I understand that I must first describe my proposed solution in a comment on this issue or via a discussion linked to this bounty, before starting my work.
|
||||
- [ ] I have reviewed the [Bounty Hunting Program Guidelines](#5211) in detail.
|
||||
|
||||
**GitHub Username**: \`YourGitHubUsernameHere\`
|
||||
|
||||
**Estimated Start Date**: \`YYYY-MM-DD\`
|
||||
|
||||
**Notes/Comments**: \`Any additional comments or notes you wish to add\`
|
||||
|
||||
By checking the boxes above, I signify my understanding and acceptance of the program rules and confirm my intent to proceed with resolving this issue as per the guidelines of the Elsa Workflows Bounty Hunting Program.
|
||||
\`\`\`
|
||||
|
||||
We value your contributions to Elsa Workflows and are excited to see the innovative solutions you bring. Let's enhance Elsa Workflows together!
|
||||
@@ -0,0 +1,40 @@
|
||||
# Pull Request Quality Standard Checklist
|
||||
|
||||
## Coding Standards
|
||||
- [ ] Follows [Microsoft's coding guidelines](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions), with the following exceptions:
|
||||
- **Use of `var`**: Use `var` for variable declarations consistently. See rationale below.
|
||||
|
||||
## Documentation
|
||||
- [ ] XML comments provided for all public members.
|
||||
- [ ] Inline comments explain complex logic.
|
||||
|
||||
## Testing
|
||||
- [ ] Unit tests cover critical functions.
|
||||
- [ ] Integration tests for major workflows.
|
||||
- [ ] High code coverage.
|
||||
|
||||
## Code Review
|
||||
- [ ] Peer review completed.
|
||||
- [ ] Passes automated code analysis.
|
||||
|
||||
## Continuous Integration
|
||||
- [ ] CI checks pass (builds, tests, style checks).
|
||||
|
||||
## Performance and Security
|
||||
- [ ] Performance is optimized where necessary.
|
||||
- [ ] Security best practices followed.
|
||||
|
||||
## Dependencies and Compatibility
|
||||
- [ ] Dependencies are managed and up to date.
|
||||
- [ ] No breaking changes without documentation.
|
||||
|
||||
### Rationale for Using `var`
|
||||
|
||||
We have chosen to use `var` consistently in our codebase for the following reasons:
|
||||
|
||||
1. **Cleaner Code**: Using `var` reduces visual clutter and makes the code cleaner and more readable. It helps avoid redundant type information, especially when the type is already apparent from the right-hand side of the declaration.
|
||||
2. **Modern IDEs**: Tools like Visual Studio and other modern IDEs provide features such as hover or tool tips that easily display variable types, alleviating concerns about readability. This makes it unnecessary to explicitly specify the type, as it can be easily inferred by the developer.
|
||||
3. **Consistency and Maintenance**: Consistent use of `var` across the codebase promotes uniformity and reduces the mental load on developers. It simplifies refactoring because changes to the type do not require updating the variable declaration.
|
||||
4. **Focus on Logic**: By using `var`, developers can focus on the logic and functionality of the code rather than getting bogged down by type details. This is particularly beneficial in complex expressions and LINQ queries where the type is often verbose and less important than the expression itself.
|
||||
|
||||
These points align with the insights from Chris Schaller's blog post "To var or not to var," which emphasizes that using `var` can lead to more maintainable and readable code in the context of modern development environments.
|
||||
Reference in New Issue
Block a user