Repository navigation
Require N approvals before a change set is approved - #2668
Merged
Merged
Conversation
# Conflicts: # src/FluentCMS.Infrastructure/ChangeSets/ChangeSetDirectory.cs
# Conflicts: # src/FluentCMS.Admin/Components/Pages/ChangeSetView.razor # src/FluentCMS.Core/Builtin/Tools/DeskTools.cs # tests/FluentCMS.Tests/Admin/ChangeSetViewTests.cs
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2645
What
The first half of RTE-05 (F-RTE #2421): a set in review needs N approvals, by N different people, before it is approved. N defaults to 1 and is configurable.
FluentCMS.Core.ChangeSets):ChangeSet.RequiredApprovals(1 to 10) andChangeSet.Approvals. EachChangeSetApprovalnames the person and the time.EnterReview(n)moves a set into review, needing n approvals.Approve(person, at)records an approval. A second approval by the same person is refused withapproval_duplicate. The N-th approval moves the set to approved.TransitionTo(Approved)from awaiting review is refused withapprovals_needed, so nothing approves a set in review except its approvals. The move from checks running to approved (routing's auto-publish) is unchanged.IChangeSetDirectory.ApproveAsync(changeSetId), andTransitionAsync(id, Approved)on a set in review goes through it.change_set.approval_recorded. The one that completes the count also appendschange_set.approved, in the same unit of work.Approvals:Required(validated at start, 1 to 10, default 1).ChangeSets.RequiredApprovals(default 1, with a check constraint from 1 to 10), and theChangeSetApprovalstable keyed by set and person. One migration for each provider.get_change_set_reviewlistsrequiredApprovalsand theapprovals, each with who and when.Why
RTE-05 says required approvals default to 1 and are configurable (N). The epic's acceptance has "a set whose level requires 2 does not publish after one approval". Routing (the next half of RTE-05) sets N from a matched review rule, and the scorer (RTE-02) from the risk level. Both need the count and the approvals on the set first.
Choices worth a look:
change_set_conflictand approves again on the fresh page.Approvals:Required, not underRouting. Routing will pass its own N for a matched rule, and this is the fallback for everything else. The issue now says so.approval_recordedis not a move inChangeSetMoves, so a set's "state since" and the desk's queues are unchanged by a partial approval.Follow-ups from the review, each in its own issue because this PR does not introduce them: #2670 (a person can still approve a set whose checks are running, through the move routing's auto-publish uses) and #2671 (Awaiting me still lists sets the reviewer already approved).
Tests
Unit:
EnterReviewtakes only 1 to 10 and changes nothing otherwise.Integration, each on SQLite and PostgreSQL:
change_set.approval_recordedthenchange_set.approved.Approvals:Requiredat 2, the first approval leaves the set in review, a repeat is refused and stores nothing, and a second person approves it. Two approval rows are stored, and the events are recorded, recorded, approved.get_change_set_reviewreturns the approval and the count to the agent.DatabaseTestsreverts and reapplies the new migration.DeskQueueTests' check that every move appends exactly one known move now leaves outchange_set.approval_recorded, as it already leaves outchange_set.operation_*: an approval is recorded next to the move it completes, not as a move.dotnet build -warnaserrorand the full suite pass locally on SQLite and against apostgres:17container. No new package and no new project reference.Checklist
v2