Epic 6 — Approval Workflow
Covers the awaiting_approval inbox, approving and rejecting stages with mandatory comments, the 7-day pause countdown that terminates unanswered gates, and the BranchProtectionViolation gate banner that blocks approval when branch-protection rules would reject the PR.
Personas: AP (primary approver) OP BU (view-only on approval gate)
Shared modules:
OptimisticMutationHelper
CorrelationChip
LastSyncedBadge
EnvProvenance
Story 6.1 — Awaiting-Approval Inbox
- As an
- AP
- I want
- to see all stages currently awaiting my approval in one place
- So that
- I never miss an approval gate that is blocking a run
Scenario: Inbox loads with pending approvals
stages.status = 'awaiting_approval' and the user has the AP role
Whenthe user navigates to /approvals and GET /approvals resolves
Thena paginated inbox is rendered showing: stage name, project name, run_id (truncated, copyable), time waiting, artifact preview link, and a countdown to the 7-day auto-expiry
Scenario: Empty inbox
| Endpoint / DB | Purpose |
|---|---|
GET /approvals | Paginated list of awaiting_approval stages |
DB stages.status = awaiting_approval | Filter source |
DB approvals.expires_at | 7-day countdown source |
Story 6.2 — Approve a Stage
- As an
- AP
- I want
- to approve a stage gate with an optional comment
- So that
- the pipeline run continues to the next stage
Scenario: Approval submitted successfully
awaiting_approval state and no BranchProtectionViolation is active
Whenthe AP clicks Approve (optionally adds a comment) and confirms
ThenPOST /approvals/{approval_id}/approve is issued with optional { "comment": "..." }; the stage card status transitions to Approved; the item is removed from the inbox
UI clones the approvals store slice into prior_state, removes the inbox item optimistically and marks the stage card Approved, then issues POST /approvals/{approval_id}/approve. On server error, the store slice is restored from prior_state exactly; the inbox item reappears; the CorrelationChip surfaces the failure. Regression: zero phantom approvals remain in the stage timeline.
| Endpoint / DB | Purpose |
|---|---|
POST /approvals/{approval_id}/approve | Record approval decision |
DB approvals.decision = approved | Written on success |
Story 6.3 — Reject a Stage
- As an
- AP
- I want
- to reject a stage gate with a mandatory comment
- So that
- the pipeline run is terminated and the reason is recorded for audit
Scenario: Rejection with mandatory comment
awaiting_approval state
Whenthe AP clicks Reject, enters a mandatory comment (blank comment blocks submission), and confirms
ThenPOST /approvals/{approval_id}/reject is issued with { "comment": "..." }; the run transitions to failed; the stage card shows a Rejected badge with the comment; the inbox item is removed
Same pattern as Story 6.2 but writes rejected decision. On rollback, the inbox item reappears with the original awaiting_approval state (regression assertion).
| Endpoint / DB | Purpose |
|---|---|
POST /approvals/{approval_id}/reject | Record rejection with mandatory comment |
DB approvals.decision = rejected | Written on success |
Story 6.4 — 7-Day Pause Countdown
- As an
- AP
- I want
- to see how long remains before an unanswered approval gate auto-expires
- So that
- I can prioritise my review queue before a run is automatically terminated
Scenario: Countdown displayed on inbox item and stage card
Scenario: Approval auto-expired — run failed
approvals.expires_at has passed
Thenthe inbox item transitions to an Expired state; the stage card shows Approval timed out; the run status shows failed
Story 6.5 — BranchProtectionViolation Gate Banner
- As an
- AP
- I want
- to see a clear blocking banner when the target branch's protection rules would reject the PR
- So that
- I know to fix the branch-protection configuration before approving rather than approving a run that will immediately fail at PR delivery
Scenario: BranchProtectionViolation banner blocks the Approve button
blocked_by: "BranchProtectionViolation"
Whenthe AP views the approval decision form
Thena red banner is shown: Branch protection violation — the target branch requires status checks that the current pipeline cannot satisfy.; the Approve button is disabled; a View branch rules link (external, opens in new tab) is provided; CorrelationChip is shown
Scenario: Rejection is still available during BranchProtectionViolation
BranchProtectionViolation banner is active
Thenthe Reject button remains enabled so the AP can terminate the run with an explanatory comment
| Endpoint / DB | Purpose |
|---|---|
GET /approvals/{approval_id} | Returns blocked_by field when gate is blocked |
DB approvals.blocked_by | Stores blocking failure class |