Skip to content

Guard review actions by state and make revert idempotent - #2653

Merged
pournasserian merged 5 commits into
v2from
2624-review-action-guards
Oct 20, 2026
Merged

pournasserian merged 5 commits into
v2from
2624-review-action-guards

Conversation

@pournasserian

@pournasserian pournasserian commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Closes #2624

What

Follow-ups from the independent review of #2581 (F-DSK, #2422).

  • Each review action is taken only from a state the panel offers it in, whatever a post claims:

    • submit from open;
    • approve from awaiting review;
    • publish from approved;
    • reject from awaiting review, checks failed or approved;
    • revert from published.

    A post for any other state gets the "moved on" refusal and changes nothing, so a set can't be approved past its checks.

  • Revert is idempotent. Before opening a revert, the page looks for a revert of this set still in progress (open, checks running, checks failed, awaiting review or approved). If there is one, a second click lands on it. ChangeSetListQuery.Reverts filters the list by the set a revert undoes.

  • Two posts at the very same instant can still both open a set. A unique index belongs to the change sets lane, and Allow one revert in progress per published change set #2646 asks for it.

  • The diff island reads through a scope of its own. The new PostgreSQL review tests exposed it: on PostgreSQL an async read can finish at once, so the set view sometimes renders ChangeSetItems before its own reads are done. The island's prerendered diff then ran on the same request DbContext and failed the page with a 500 or a stall. This is the Run the header counts in their own scope so signed-in pages load on PostgreSQL #2642 rule applied to the last component that loads data beside its page. It is also the likely cause of the intermittent SignedInPageTests hang on v2.

Tests

  • ReviewActionTests runs on SQLite and PostgreSQL.
  • The approve test asserts the stored state is approved.
  • Each of these posts carries the set's current Seen and is refused, leaving the state unchanged:
    • an approve for a set in checks running, checks failed, open or published;
    • a publish from awaiting review;
    • a revert from approved;
    • a submit from checks failed;
    • a reject from open.
  • A second revert of the same published set lands on the first, and only one revert set exists.
  • Every action's form is refused without the anti-forgery token, and an anonymous post of each goes to sign-in.
  • Without the guards, the refusal and revert tests fail.

dotnet build -warnaserror, the unit and bUnit suite and the review, list and queue tests on SQLite and PostgreSQL pass locally. No new package and no new project reference.

Checklist

  • Linked to an issue a maintainer has confirmed
  • Targets v2
  • Tests or docs updated where behaviour changed
  • No tool-generated co-author trailers or footers

@pournasserian pournasserian added this to the M1 Governance core milestone Oct 19, 2026
@pournasserian pournasserian added v2 FluentCMS v2 (v2 branch) type:bug Defect area:desk Review desk (DSK) labels Oct 19, 2026
@pournasserian
pournasserian merged commit 6ad7629 into v2 Oct 20, 2026
2 checks passed
@pournasserian
pournasserian deleted the 2624-review-action-guards branch October 20, 2026 00:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:desk Review desk (DSK) type:bug Defect v2 FluentCMS v2 (v2 branch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard review actions by state and make revert idempotent

1 participant