Group and vote steps: group steps that cannot reach their vote counts, and a voting deadline fixed on the definition

Description

Depends on (merged to dev first): AP-2450 (excluded users refused on group steps), AP-2449 (GroupVotesField; field group membership changes continue the approval through continueAfterStepsChange), AP-2380 (Rovo, GroupStepProgress, VotingDeadline, RecipientsByRole, votes refused after the deadline). AP-2466 (vote step "Period after the step starts") is not in dev; this change handles fixed dates only and keeps its footprint in the vote deadline code small for the later merge.

Problem

  1. A group step can stay "In progress" for good.

    • The step needs a number of votes "to pass" and "to fail" (1 to 999). The editor only checks that both are at least 1, the server nothing; neither is compared with the group size.

    • Each member votes once. An approval counts towards "to pass", a rejection towards "to fail", an abstain towards both. The step ends only when one of the two numbers is reached.

    • When everyone who can vote has voted and neither number is reached, nothing ends the step: "to pass" is higher than the number of voters (group of 3, 5 to pass, all approve), or the votes split (group of 2, 2 to pass, 2 to fail, one approves, one rejects). Exclusions (the approval creator, chosen users, users from work item fields in Jira) lower the number of voters, so a definition that worked can start to get stuck.

    • Reminders reach no member, Rovo shows the step as waiting for a decision, and group steps cannot be reassigned. Only a decision deadline (Incomplete), a membership change, the parallel group ending without the step, or rerunning or deleting the approval moves it on.

    • The votes fields are also shown for Notification steps, where they have no effect: on group steps and, in Jira, on work item user field steps with "Treat users as a group".

  2. The vote step "Voting deadline" can be a fixed date and time saved on the definition. Every approval started from the definition gets the same date.

    • After that date, every new approval opens a vote step whose voting is already closed. Voters are still asked to vote, but a vote is refused with "Voting on step '…' closed at …", and the moderator gets "voting expired" right away and decides with no votes. Rovo hides the vote and does not remind voters.

    • The "must be in the future" check runs only in the vote step form. The definition can still be saved with the past date. The Web API accepts any date.

Group and vote steps exist in both products; the behaviour is the same in Jira and Confluence.

Change

  1. Group steps that cannot reach "to pass" or "to fail".

    • Members who can vote: group members who are not excluded and have not voted. While any of them is left, the step waits as today. When none is left and neither number is reached:

      • only approvals and abstains, at least one approval: the step is approved;

      • only rejections and abstains, at least one rejection: the step is rejected;

      • in both cases Activity says the required number exceeded the voters ("Approved by all 3 members who could vote; 5 approvals were required") and the approval continues as after any decision;

      • otherwise (approvals and rejections, only abstains, or no votes) the step is blocked.

    • A blocked step stays in progress. The approval panel, approvals lists, approval details, Activity, Slack and Rovo show the reason, e.g. "Blocked: needs 2 approvals or 2 rejections, but nobody is left to vote".

    • The user who started the approval gets one notification when the step becomes blocked, through the new editable message template "Group step blocked notification" (Jira and Confluence defaults). It is not sent when the approval was started by a JSM portal customer, who can never decide it.

    • A blocked step can be decided in the app UI, with a required comment: the starter can reject it, and approve it only when not excluded from the step; global admins can approve or reject it, unless they are excluded from the step (then reject only). Nobody else, never JSM customers, and not when the approval can no longer be decided (archived, outdated, deadline passed). Activity records "Decided for a blocked group by …" with the comment, and the approval continues. No new approval version is created.

    • The block clears by itself when a membership change makes a number reachable again; a membership change can also end the step as above. A user field group whose field is cleared goes back to waiting for its field, not blocked.

    • A decision deadline, when set, still makes the approval Incomplete, blocked or not.

    • The check runs after each vote, when the step starts and when members change. Live Jira and Confluence groups are re-checked when the app re-reads them for pending approvers, from the app's cached member list; no extra Atlassian calls.

    • The votes fields are hidden for Notification steps (group steps and user field steps with "Treat users as a group").

    • The definition editor warns when "to pass" or "to fail" is higher than the current number of members of a chosen group (after exclusions). It does not block saving.

    • The server checks the bounds 1 to 999 for every saved definition, from the editor and the Web API. In the Web API v2, requiredVotes and requiredRejectVotes may be left out and then count as 1; an explicit value must be 1 to 999.

  2. A fixed voting deadline that has passed.

    • A vote step that starts with its fixed deadline already passed opens voting without a deadline (also after a reload). The moderator's start message, and its version for delegates, gets the new template variable votingDeadlineNote: "The fixed voting deadline … had passed when this step started, so voting has no deadline." The variable is empty in every other case, so customised templates are unaffected until it is added to them.

    • The server refuses a fixed date in the past when the definition is saved, in the editor and in the Web API.

Decisions taken

  1. The step-blocked message is a new editable template, like every other message to the starter.

  2. A blocked step is decided in the app UI only; Rovo shows the blocked state and who can decide it.

  3. Parallel groups that split without a result stay a separate ticket.

  4. No one left to vote and only abstains: blocked.

  5. a) Voting opens without a deadline; the moderator is told through votingDeadlineNote.

  6. Moderator delegates now also get the vote results and the voting deadline note in their messages.

Fixes included

  • An abstain that completed a group step's rejections left the approval rejected without running rejection finalisation.

  • Duplicate drops a fixed voting deadline that has already passed.

  • The Jira dashboard gadget offers blocked group decisions to global administrators.

  • The editor warns about a group with no voters when Skip when empty is off.

  • Stored vote counts below 1 are set to 1 on deploy.

  • A failed read of group members keeps the step waiting instead of failing the vote.

  • Deciding a blocked group step from the panel asks for decision confirmation (PIN, email code or authenticator app) when it is turned on, like every other decision, and the step records how the decision was confirmed. The approval starter or a global administrator can request the email code for that decision.

  • The blocked reason, the outcome note, the Rovo explanation and the activity use the decision names set in the decision button settings.

Out of scope

  • Removing a step from a running approval: the step removal ticket.

  • Parallel groups that split without a result.

  • The voting deadline timer is kept in memory; a server restart before the deadline drops the moderator's "voting expired" message.

Known limits

  • Pre-deploy approvals started by a JSM customer that are still in progress may send that customer one Approval blocked message.

  • A stored vote count above 999 (only old Web API saves could create one) is refused on the next save, with a message naming the step.

Acceptance criteria

  • Group of 3, 5 to pass, 5 to fail: all approve, the step is approved and Activity says 5 were required; two reject and one abstains, it is rejected.

  • Group of 2, 2 to pass, 2 to fail: one approves, one rejects; the step is blocked, every view listed above shows the reason, and the starter gets one notification.

  • The starter rejects a blocked step with a comment; the approval continues. Without a comment it is refused.

  • A starter excluded from the step can reject but not approve; one not excluded can approve. A global admin can do both unless excluded; another user cannot.

  • Jira, group from a work item field: adding a user clears the block; removing the only member who had not voted ends the step as above. A blocked approval becomes Incomplete when its decision deadline passes.

  • Notification group steps and Notification user field steps with "Treat users as a group" show no votes fields.

  • The editor warns when "to pass" or "to fail" exceeds the group size and still saves; 0 or 1000 is refused, from the editor and the Web API.

  • A vote step whose fixed deadline passed before it starts opens voting without a deadline, and voters are not asked to vote on a closed vote.

  • A definition with a fixed voting deadline in the past cannot be saved, from the editor or the Web API.

Manual tester notes

  • Group of 3, 5 to pass and 5 to fail, all three approve: the step is approved, and the panel and Activity say "Approved by all 3 members who could vote; 5 approvals were required". With two rejects and one abstain the step is rejected. A Notification group step shows no such note.

  • Group of 2, 2 to pass and 2 to fail, one approve and one reject: "Blocked: …" shows in the panel, the approvals list ("Blocked" lozenge) and the approval details; Rovo says blocked and who can decide; the starter gets one "Approval blocked" message on each enabled channel. Repeat with an approval started from the JSM portal by a customer: no message.

  • The starter rejects the blocked step: refused without a comment, then works with one; Activity shows "Decided for a blocked group by …"; the Slack message, if enabled, lists the decider and the comment.

  • Starter excluded from the group: only Reject, also when the starter is a global admin. Starter not excluded: Approve and Reject. Another global admin: both. Any other user and a portal customer: no buttons.

  • Jira, group from a work item user field: adding a user clears the block; removing the only member who had not voted ends the step; clearing the field shows the waiting-for-field state, not Blocked; a blocked approval with a decision deadline becomes Incomplete when the deadline passes.

  • Live Jira or Confluence group: after the last member who had not voted is removed, the step changes at a later re-check, up to about a day and a half later (members cached for 24 hours, re-checked every 12 hours); ask for shorter values of ap.cache.approval-data.actionable-group-ttl-seconds and ap.pending-approver.stale-refresh-after-hours on QA.

  • Notification steps, and user field steps with "Treat users as a group", show no votes fields. The editor warns when the numbers exceed the chosen group's members and still saves; 0 or 1000 is refused.

  • Vote step with a fixed deadline a few minutes away, started after it passes: voting is open and still shows no deadline after reloading the panel; the moderator's start message and a moderator delegate's say the deadline had passed and voting has no deadline. A deadline still ahead shows no such line. Saving a definition with a past fixed deadline is refused with the step name.

  • Repeat a group case and the deadline case in Confluence.

  • Turn on decision confirmation with a PIN. Make a group step blocked (its vote counts can no longer be reached), then decide it from the panel as the approval starter: the PIN prompt appears, the decision is recorded only with the correct PIN, and the decided step shows the confirmation mark.

  • Repeat with confirmation by email code: the code email reaches the starter and the decision is recorded with that code.

  • Rename the decision buttons in the settings (for example Approve to Signed off): the blocked step's reason and its outcome note use the new names.