Skip to content

Delivery

The Delivery stage of the product process (DPPD) is where an agreed Plan becomes shipped, operated software. This page is the canonical description of how Delivery runs day to day:

  • the Jira statuses a piece of work moves through, what each means, and who moves it,
  • the automations that keep Jira in sync with GitHub and the deploy pipeline,
  • how the Delivery backlog is organised and prioritised.

It ties together the mechanics documented elsewhere on this site: PR reviews, Branching & releases, and Epic (feature) branches.

One ticket per change

No code lands without a PPL ticket in the PPL project on percihealth.atlassian.net. The ticket key drives the branch name and PR title (see PR reviews) and links the work back to its rationale. Most tickets originate from the Delivery stage of DPPD: a Plan becomes epics and tickets that then move through the statuses below.

Source of truth: live Jira workflow

The statuses and transitions on this page were mapped directly from the live PPL Jira workflow on 19 June 2026. Jira is the source of truth; if the workflow changes, update this page in the same change (see the tip at the bottom of the Engineering index).

Work item types

The PPL project uses this issue hierarchy. The Frontend/Backend split is what lets an epic land the backend first and the frontend after it (see Epic branches).

Level Types
Epic (L1) Epic
Standard (L0) Story, Task, Bug, Spike, Frontend, Backend
Sub-task (L-1) Sub-task, Design, Frontend Subtask, Backend Subtask

Status flow

flowchart TD
  start((Start)) -->|Create| open["Open<br/>(To Do)"]
  planning["Ready for Planning<br/>(To Do)"] -->|Ready for work| open
  open -->|Back to planning| planning
  open -->|Start work| wip["Working on it"]
  wip -->|Ready for code review| review["Ready for review"]
  review -->|Changes requested| wip
  review -->|Ready to test| rft["Ready for Testing"]
  rft -->|Start testing| intest["in Test"]
  intest -->|Test passed| merge["Ready to Merge"]
  intest -->|Bugs found| wip
  merge -->|"Merge to Staging ⭐"| staging["On Staging"]
  staging -->|Regression passed| release["Ready for Release"]
  release -->|Released to prod| done["Done"]
  wip -->|No testing needed| done

  blocked["Blocked"] -->|Work unblocked| wip
  blocked -->|Test unblocked| intest

  classDef terminal fill:#d4edda,stroke:#28a745,color:#000;
  classDef todo fill:#e2e3e5,stroke:#6c757d,color:#000;
  class done terminal;
  class planning,open todo;

⭐ = automated transition (see Automations).

Five statuses are global, reachable from any status (the Any badge in Jira): Open, Ready for Testing, On Staging, Blocked and Closed. New tickets are created in Open. Blocked leaves again via Work unblocked (to Working on it) or Test unblocked (to in Test).

Statuses

Each status maps to one of Jira's three categories: To Do (blue-grey), In Progress (yellow) and Done (green).

Ownership confirmed with the team

The Moved on by column is who actually performs each forward transition, confirmed with the team on 19 Jun 2026.

Status Category Meaning Moved on by
Ready for Planning To Do A ticket that needs (more) planning before it can be picked up. New tickets are created in Open; Back to planning sends one here and Ready for work returns it to Open. Product (Ready for work back to Open)
Open To Do The Delivery backlog: created and ready to be picked up. Engineer or Designer (Start work; depends on the ticket type)
Working on it In Progress Being implemented. The engineer (or designer) creates a branch in the relevant project, e.g. PPL-121/change-auth. Assignee
Ready for review In Progress A PR is raised and ready for code review by another engineer. Engineer, once review passes
Ready for Testing In Progress Review has passed; the change is handed to QA. QA
in Test In Progress QA is actively testing the change on the feature-branch preview (see Testing). QA: Test passed to Ready to Merge, or Bugs found back to Working on it
Ready to Merge In Progress Tested and approved. The ticket waits here for the engineer to merge the PR into develop; the merge then auto-moves it to On Staging (see Automations). Engineer (merges the PR)
On Staging In Progress Merged to develop. A release branch is cut and QA runs regression against it. QA, once regression passes
Ready for Release In Progress Regression passed on the release branch; waiting to be released to production. Release manager (Celina)
Done Done Released to production. n/a (terminal)
Blocked In Progress Work cannot proceed. Reachable from any status; returns via Work unblocked (to Working on it) or Test unblocked (to in Test). Anyone
Closed Done Terminal state for cancelled / won't-do work (distinct from Done); not used much in practice. Anyone

CONFIRM: status casing

Two status names carry inconsistent casing in Jira: Ready for review (lower-case "review") and in Test (lower-case "in"). They are written here exactly as configured. Worth tidying in Jira for consistency; flag if you want that done.

Transitions

The workflow uses named transitions (the buttons you click in Jira). The transition name differs from the destination status, and the names are what automations hook onto, so they are listed explicitly here.

Transition From → To What it means
Create (start) → Open A new ticket is created directly in Open.
Ready for work Ready for Planning → Open Planning done; ready to be picked up.
Back to planning Open → Ready for Planning The ticket needs (more) grooming before work starts.
Start work Open → Working on it Engineer (or designer) begins implementation.
Ready for code review Working on it → Ready for review PR raised, ready for review.
Changes requested Ready for review → Working on it Reviewer asked for changes; back to the author.
Ready to test Ready for review → Ready for Testing Review passed; handed to QA.
Start testing Ready for Testing → in Test QA begins testing.
Test passed in Test → Ready to Merge QA testing passed.
Bugs found in Test → Working on it QA found bugs; back to the author.
Merge to Staging Ready to Merge → On Staging The engineer merges the PR into develop; the automation performs this transition (see Automations).
Regression passed On Staging → Ready for Release Regression on the release branch passed; queued for release.
Released to prod Ready for Release → Done Change released to production.
No testing needed Working on it → Done Fast-path for non-code / admin tickets only (docs, ops). Code must not skip review / merge / release.
Work unblocked Blocked → Working on it Unblocked; resume implementation.
Test unblocked Blocked → in Test Unblocked; resume testing.
Open / Ready for Testing / On Staging / Blocked / Closed (global) → that status Global transitions, reachable from any status (the Any badge). Closed is the cancelled / won't-do terminal.

Merging into develop is automated

A ticket sits in Ready to Merge until the engineer merges its PR into develop. That merge triggers the automation that moves it to On Staging and stamps the fix version (see Automations). Releases are cut from develop, see Branching & releases.

Notable paths

  • Fast-path (No testing needed). For non-code / admin tickets only (docs, ops, config that does not ship as a code release) a ticket may go straight from Working on it to Done. Code changes must not use it: Done means released to production, so code still goes through review, merge and release even when QA adds little.
  • Review rework (Changes requested). A PR that needs changes goes from Ready for review back to Working on it, so review and rework iterate without losing the ticket's place.
  • Test rework (Bugs found). If QA finds bugs, in Test goes back to Working on it rather than forward.
  • Re-planning (Back to planning). A ticket in Open that needs more grooming goes back to Ready for Planning; Ready for work returns it to Open.

Definition of Done

A code ticket is Done only when the change is released to production and:

  • the PR is reviewed and approved by another engineer (see PR reviews);
  • comprehensive tests ship with the change: unit / widget coverage and an automated end-to-end test that captures the user story (the flow that proves the feature works), so it joins the regression suite. The CI gate must be green (coverage non-regression, analyze clean);
  • it was verified in the feature-branch preview during in Test, and regression passed on staging;
  • the acceptance criteria are met;
  • any documented behaviour or process it changes is updated in the same PR (treat a missing doc update like a missing test);
  • the ticket carries the fix version of the release it shipped in.

Non-code / admin tickets (the No testing needed fast-path) are Done when the work itself is complete; the code-specific items above do not apply.

Automations

Delivery transitions are kept in sync with GitHub by Automation for Jira flows (Jira's native automation), scoped to the Perci Platform project. Documented flows:

Pull request merged to develop -> On Staging

Field Value
Trigger Pull request merged
Conditions {{pullRequest.destinationBranch}} equals develop, and {{pullRequest.sourceBranch}} contains {{issue.key}}
Actions 1. Transition the work item to On Staging. 2. Edit work item fields: set Fix versions to Next.
Owner / actor Luke Dixon / Automation for Jira
On error E-mail the owner once when the flow starts failing after success.

When the PR for a ticket (its source branch contains the ticket key) is merged into develop, the ticket moves from Ready to Merge to On Staging and its fix version is set automatically. Merging the PR is the engineer's action; the status change and the fix version are handled for you. (The underlying On Staging transition is global, so it also applies if the ticket was in another status when the PR merged.)

Fix version lifecycle

The automation always stamps the placeholder version Next, never a real version number. The release itself is cut by a GitHub workflow that takes the release number manually; that workflow operates only on GitHub (it creates the release branch from develop) and does not touch Jira. Re-stamping every Next ticket to the actual release version (for example 1.2.3) is therefore a manual step in Jira at release time. That re-stamp is what ties each shipped change to the release it went out in. See Branching & releases.

Manual gap, and other flows

The Next -> real-version re-stamp in Jira is manual today: the GitHub cut-release workflow does not update Jira, so someone updates the Next tickets by hand at release time. It is an obvious candidate for automation. This is also the only Jira automation captured so far; other transitions (for example Released to prod -> Done on a production deploy) are manual unless a flow is added.

Build these next (priority order)

The live flow proves GitHub PR events can drive Jira transitions and field edits via smart values, so these are the natural next steps. Build top-down; the first two remove the most manual toil.

# Automation Removes
1 Re-stamp fix version at release cut the manual Next -> version sweep
2 Released to prod -> Done dragging every shipped ticket to Done
3 PR opened -> Ready for review a manual drag per PR
4 Review approved -> Ready for Testing a manual drag per approval
5 Changes requested -> Working on it tickets silently stuck in review
6 Start work on first push (+ assign) unassigned, stale-Open tickets

Specs

Each is an Automation for Jira rule (same engine as the live flow). {{...}} are smart values; always guard on the current issue status so a rule never drags a ticket backwards.

1. Re-stamp fix version at release cut

  • Trigger: Branch created.
  • If: branch name matches release/(.*); capture the version from {{branch.name.match("release/(.*)")}}.
  • Then: for each issue in JQL project = PPL AND fixVersion = "Next", create the version if needed and set Fix versions to the captured version.
  • Alternative (preferred): do it in the cut-release GitHub workflow, which already knows X.Y.Z, by calling the Jira REST API. Pick one, not both.

2. Released to prod -> Done

  • Trigger: Pull request merged.
  • If: {{pullRequest.destinationBranch}} equals main and {{pullRequest.sourceBranch}} matches release/(.*) (also allow hotfix/*); capture the version.
  • Then: for each issue in JQL project = PPL AND fixVersion = "<captured>" AND status = "Ready for Release", run the Released to prod transition to Done.

3. PR opened -> Ready for review

  • Trigger: Pull request created.
  • If: {{pullRequest.sourceBranch}} contains {{issue.key}}, the PR is not a draft, and the issue status is Working on it.
  • Then: run the Ready for code review transition to Ready for review.
  • Note: PRs may target the epic branch, not develop, so do not condition on the destination branch here.

4. Review approved -> Ready for Testing

  • Trigger: Pull request reviewed, review state = approved.
  • If: {{pullRequest.sourceBranch}} contains {{issue.key}} and the issue status is Ready for review.
  • Then: run the Ready to test transition to Ready for Testing.

5. Changes requested -> Working on it

  • Trigger: Pull request reviewed, review state = changes requested.
  • If: {{pullRequest.sourceBranch}} contains {{issue.key}} and the issue status is Ready for review.
  • Then: run the Changes requested transition to Working on it.

6. Start work on first push (+ assign)

  • Trigger: Commit created (or Branch created).
  • If: the commit message / branch name contains {{issue.key}} and the issue status is Open.
  • Then: run the Start work transition to Working on it and set Assignee to the commit author (needs linked GitHub / Jira identities).

Feasibility

The merge-based flow already in place shows the GitHub integration works. The review-state triggers (3-5) need the pull request reviewed event in Automation for Jira's GitHub integration; if it is not available, drive them from a small GitHub Action calling the Jira REST API. The release-side rules (1-2) are most reliable run from the cut-release / release-merge GitHub workflows, which already know the version.

Backlog

The backlog is a single ranked, groomed queue, not an archive. You finish a ticket, go to the backlog, and take the next one off the top. We run it Kanban-style: continuous pull, no sprints.

What lives where

  • Ready for Planning (the Kanban backlog): still being groomed, off the active board.
  • Open (the To Do column): groomed and ready to pull; the top is next.
  • Closed: stale or won't-do; a terminal state in the Done / Released column.

A ticket is ready (belongs in Open) when it has a clear description, an Epic, a Component, a Priority, and any blockers linked. Otherwise it stays in Ready for Planning.

Order is priority

The ready queue is rank-ordered (drag order): the top of To Do (Open) is what to pick next, and the Priority field drives that order. Don't scan the list and cherry-pick; take the highest-ranked ticket you are eligible for.

Area: the Component field

Every ticket carries a Component marking the part of the platform it touches. Because the backend and all the frontends share one board, the Component is what makes "which ticket is for me?" obvious. The components are:

Component Area Mostly picked up by
Member App The perci-platform-members Flutter app Frontend
Clinical App The perci-platform-clinicians Flutter app Frontend
Website The brochure site (percihealth.com) Frontend
Backend Functions, BFFs, Medplum bots, and the web-professionals portal Backend
Infrastructure CI/CD, deploys and platform plumbing Backend / platform
Admin Cross-cutting admin and ops (e.g. documentation) Any

Component is used rather than the issue type because it applies to every issue type: a Bug or Spike belongs to an area just as much as a Story does. Board quick filters (one per component, plus Mine and Unassigned) and Epic swimlanes key off Component, so "what is the next Backend (or Member App) ticket I can pick up?" is one click.

Admin is the catch-all

Admin covers cross-cutting and internal work that is not tied to a specific app (for example documentation), so anyone may pick it up. The web-professionals React portal is treated as Backend.

The pick rule

When you finish a ticket, take the next one in this order:

  1. Stay on your epic. The top-ranked ready ticket of your component(s) in the epic you are currently working. This keeps you in context.
  2. Otherwise move epic. If your component(s) have nothing ready in that epic, take the top-ranked ready ticket of your component(s) in the highest-ranked epic that still needs it.
  3. Rank breaks ties.

Epics are not formally assigned to people; this is a soft "prefer your current epic" rule, to cut context switching without locking anyone to an epic.

Frontend / backend ordering within an epic

Backend usually lands before the app work that depends on it (see Epic branches). So within an epic:

  • Rank the Backend ticket above the app ticket (Member App / Clinical App, etc.) that needs it.
  • Link the app ticket is blocked by the Backend ticket. A blocked ticket is not ready, so it will not be picked until its backend dependency is done.

Board setup

The PPL board is a Kanban board with the backlog enabled. Configure it under Board settings so the queue and the areas are obvious:

  • Backlog on. The Kanban backlog is enabled and holds Ready for Planning (grooming); Open sits in the To Do column as the ready-to-pull queue, rank-ordered (top is next).
  • Quick filters (one click to your work):
    • Member App: component = "Member App"
    • Clinical App: component = "Clinical App"
    • Website: component = Website
    • Backend: component = Backend
    • Infrastructure: component = Infrastructure
    • Admin: component = Admin
    • Frontend: component in ("Member App", "Clinical App", Website)
    • Backend / platform: component in (Backend, Infrastructure)
    • Mine: assignee = currentUser(); Unassigned: assignee is EMPTY
  • Swimlanes by Epic, so each epic is a row. With a component quick filter you see "the next Member App ticket in epic X" at a glance.
  • Card layout and colours. Show Components, Priority and Epic on the card, and colour cards by Component so the areas pop.
  • Columns (one per status, as on the Scrumban board): Kanban backlog = Ready for Planning; To Do = Open; In Progress = Working on it; Blocked = Blocked; Ready for Review = Ready for review; Ready to Test = Ready for Testing; In Test / Review = in Test; Ready to Merge = Ready to Merge; On Staging = On Staging; Ready for Release = Ready for Release; Done / Released = Done + Closed.

Keeping the backlog healthy

  • One-time clean-up. Triage the current pile: Closed for stale or irrelevant, Ready for Planning for the rest, then add Component / Epic / Priority and rank whatever survives into Open.
  • Weekly refinement. Product and the leads keep the top couple of weeks of the backlog groomed and ranked; below that can stay rough.
  • Stale guard. A saved filter flags stale Open tickets for re-triage so the queue does not silt up again: project = PPL AND status = Open AND updated <= -90d ORDER BY updated ASC (optionally a Jira automation comments on or labels them).

Relation to DPPD

The backlog is fed by the Plan stage of DPPD: a Plan becomes an Epic plus its tickets, which enter the backlog (in Ready for Planning until groomed, then Open). Prioritising across epics is a Product / Plan decision; the backlog rank reflects it.

Proposed: leaner status model

Proposal, not yet applied

The statuses above are the live workflow (the source of truth). This section is a proposed improvement to discuss; nothing here is in Jira yet.

Why. Six in-progress queue states after coding (Ready for review -> Ready for Testing -> in Test -> Ready to Merge -> On Staging -> Ready for Release) inflate cycle time, each is a manual drag, and two names are inconsistently cased.

Proposed active states: Ready for Planning, Open, In Progress, In Review, In QA, On Staging, Ready for Release, Done (plus Blocked, Closed).

Current Proposed Change
Working on it In Progress rename: matches the board column, drops the colloquial name
Ready for review In Review rename + fix casing
Ready for Testing + in Test In QA merge two queue states into one (keep both only if the QA queue is genuinely long, which is itself worth fixing)
Ready to Merge (removed) enable PR auto-merge on approved + green instead of a manual hand-off state
On Staging On Staging keep (or rename In Regression for an action-style name)

Net 12 -> ~9 statuses: fewer manual moves and honest cycle-time. If you keep the current shape, at minimum fix the casing (Ready for Review, In Test) and rename Working on it -> In Progress, then update the automations and board columns to match.