Turn GitLab merge-request reviews into planned, measurable Jira work — from review request, through the review/approval gates, to the hand-off to integration.
Code review for prplOS lives entirely in GitLab. A contributor opens an MR, a reviewer asks for changes, the contributor delivers, the reviewer looks again — and that round-trip repeats, often several times, with no visibility outside the MR.
Nothing is plannable: you can't put “review this delivery” into a sprint, you can't see how many cycles an MR burned, and you can't tell whether time is lost waiting on the developer or on the reviewer. One delivery routinely spans several MRs, which makes the missing delivery-level view worse.
Jira is the system of record — the plannable, auditable surface. n8n is the glue at the two edges: it fans intake out into the structure, and it syncs GitLab MR state onto the tickets. Jira Automation handles the in-Jira assignee flips.
flowchart TD Dev(["Developer"]) -->|fills intake Form| RR["Review Request
(intake record)"] RR -->|n8n fan-out| Epic[["Delivery — Epic"]] Epic --> CR1["Code Review · MR a"] Epic --> CR2["Code Review · MR b"] Epic --> CR3["Code Review · MR c"] Epic --> INT["Integration
(1 per delivery)"] GL{{"GitLab MR webhooks"}} -->|n8n sync| CR1 GL -->|n8n sync| CR2 GL -->|n8n sync| CR3 CR1 -.->|all Approved → rollup| INT CR2 -.-> INT CR3 -.-> INT classDef rec fill:#eef,stroke:#88a,color:#223; classDef int fill:#e8f6ee,stroke:#5aa97a,color:#143; class RR rec; class INT int;
| Concern | Owner |
|---|---|
| Intake fan-out (Epic + Integration + Code Review×N, enriched from GitLab API) | n8n |
| GitLab events → Code Review transitions; rollups → Integration ready + integrated | n8n |
| Assignee flips on every transition | Jira Automation |
| branch → release-track + fixVersion mapping | n8n config table |
| Project, issue types, fields, workflows, Form | Jira (built via REST) |
Three tiers plus a per-delivery integration item. The Form can only create the task-level Review Request; n8n reads it and builds the rest.
| Type | Level | Created by | Purpose |
|---|---|---|---|
| Review Request | task | the Form | durable record of the ask (who / what / which MRs) |
| Delivery (= Epic) | epic | n8n | the container; groups a delivery's MRs |
| Code Review (new type) | task | n8n | one per MR — the review/approval workhorse |
| Integration (new type) | task | n8n | one per delivery — the integration team's item |
| Field | Type | On | Set by | Purpose |
|---|---|---|---|---|
| Developer | user | Code Review | n8n (GitLab author) | MR author; ball on Changes Requested |
| Reviewer | user | Code Review | triage lead | first gate; ball on In Review |
| Approver | user | Code Review | triage lead | second gate; ball on In Approval |
| Integrator | user | Integration | integration team | ball on Ready for Integration |
| GitLab Project | short text | Code Review | n8n | lookup key (path) |
| MR IID | number | Code Review | n8n | lookup key |
| MR URL | URL | Code Review | n8n | link (+ remote link) |
| Target Branch | short text | Code Review | n8n | from MR target branch |
| MR merged at | date | Code Review | n8n (merge webhook) | per-MR merge fact; rolled up to set Integration → Integrated |
| Release Track | select | Code Review | n8n (derived) | dev / production / … — grouping & metrics |
| fixVersion | versions (multi) | Code Review | n8n (derived) | target release(s); multi-valued for backports |
| Blocked reason | short text | both | n8n / human | why an item is flagged or blocked |
| Flagged (native) | checkbox · customfield_10021 | both | n8n / human | at-risk or blocked indicator (value Impediment); rolls up to Epic |
| Due date (native) | date · duedate | Code Review | n8n (by priority) | explicit deadline; complements the SLA |
Contributors must have Jira accounts, so Assignee is always a real, actionable user. Role fields (Developer/Reviewer/Approver/Integrator) are created single-user — the instance's existing multi-user Reviewers/Approvers fields are the wrong cardinality for the flip.
One state = one person's court, which is what the Assignee auto-flip keys off. A change request at either gate returns the ball to the Developer and re-enters via re-review.
stateDiagram-v2 [*] --> ToReview: n8n creates ToReview --> InReview: triage done (Reviewer + Approver set) InReview --> InApproval: reviewer approves InReview --> ChangesRequested: reviewer requests changes InApproval --> Approved: approver approves InApproval --> ChangesRequested: approver requests changes ChangesRequested --> InReview: dev delivers (always re-review) Approved --> ChangesRequested: reopen (integration failure) ToReview --> Cancelled InReview --> Cancelled ChangesRequested --> Cancelled InApproval --> Cancelled Approved --> [*] Cancelled --> [*]
| State | Category | Assignee (ball) |
|---|---|---|
| To Review | To Do | triage lead |
| In Review | In Progress | Reviewer |
| Changes Requested | In Progress | Developer |
| In Approval | In Progress | Approver |
| Approved | Done | — |
| Cancelled | Done | — |
To Review → In Review requires Reviewer and Approver to be set.One Integration ticket per delivery, born with the Epic in Waiting for Review, auto-promoted when the whole delivery is approved. Same project, separate board, so the integration team plans independently.
stateDiagram-v2 [*] --> WaitingForReview: created with the Epic WaitingForReview --> ReadyForIntegration: all Code Review children Approved (n8n) ReadyForIntegration --> Integrating: integrator starts Integrating --> Integrated: n8n — all delivery MRs merged in GitLab Integrating --> WaitingForReview: failure → reopen Code Review item(s) Integrated --> [*]
Per Code Review item, multi-valued so a dev MR and its production backport coexist. n8n owns a small branch → track + version table.
| Track | Branch | fixVersion |
|---|---|---|
| development | latest-24.10 | 5.0.0 |
| production | mainline-23.05 / 4.0.y | 4.0.x |
Lightweight — per-MR detail comes from the GitLab API, not the form.
| Field | Maps to |
|---|---|
| Delivery title * | Request summary + Epic name |
| Context / description * | why / what |
| MR URLs * (one per line) | one Code Review per line |
| Related feature / epic | optional link |
| Priority / review-by | optional |
| Suggested reviewers | optional hint |
GET the GitLab MR → create a Code Review
item (parent = Epic; Developer, GitLab Project, MR IID, MR URL + remote link,
Target Branch, Release Track, fixVersion), in To Review, default-assigned to the triage lead.… (SCDX-1234)), idempotently — lights up GitLab's native Jira Dev panel
and lets sync resolve by title. Needs a GitLab write/api token; per-MR key, not the Epic's.| Rule | On transition to | Action |
|---|---|---|
| R1 | In Review | Assignee ← Reviewer |
| R2 | Changes Requested | Assignee ← Developer |
| R3 | In Approval | Assignee ← Approver |
| R4 | Ready for Integration | Assignee ← Integrator |
| R5 | To Review (created) | Assignee ← triage lead |
No JSM = no native SLA engine, so n8n replicates it. Reaction time = age of the current status (one state = one role's court), checked against a priority × waiting-state matrix (tunable n8n config):
| Priority | In Review / Changes Req. / In Approval | Ready for Integration |
|---|---|---|
| Supermegacritical (S-1) | 4h | 4h |
| Blocker (S0) | 4h | 4h |
| Critical (S1) | 1d | 1d |
| High (S2) | 2d | 3d |
| Low (S3) | 4d | 5d |
prpl-foundation/prplos needs Maintainer; a
GitLab write/api token is needed for the MR-title writeback; n8n needs a
reachable endpoint; the GitLab↔Jira user map must exist.SCDX (gh-scrum-template) + single-user role fields + GitLab/Blocked-reason fields + versions. Reversible.| # | Decision |
|---|---|
| 1 | 3-tier model: Review Request → Delivery (Epic) → Code Review (custom); + Integration (custom) |
| 2 | Roles: Assignee (auto) + Developer + Reviewer + Approver, all Jira users; both Reviewer & Approver must approve |
| 3 | Gates sequential: Reviewer → Approver; either change request returns ball to Developer |
| 4 / 4a / 4b / 4c | Integration = separate ticket · per-delivery · upfront Waiting → auto-ready · same project, separate board |
| 5 | CR workflow as §5; single Changes Requested state; always re-review |
| 6 | Integration failure → reopen Code Review item(s) |
| 7 | Assignee flips via Jira Automation |
| 8 | Versions layered: fixVersion + Release Track + Target Branch |
| 9 | Reviewer / Approver assigned at Jira triage |
| 10 | MR capture: multi-line, one URL per line (gate-event mapping deferred) |
| 11 | Lookup key: GitLab Project + MR IID |
| 12 | Contributors must have Jira accounts |
| 13 | Triage ball: configured triage lead |
| 14 | Project key SCDX, name “Source Code Delivery (PoC)”, company-managed Scrum |
| 15 | MR↔ticket link: per-MR key as MR title suffix (SCDX-…) + immutable MR IID/Project/URL fallback |
| 16 | Blocked / at-risk: reuse native Flagged (Impediment) + Blocked reason field — no Blocked status |
| 17 | Deadlines/SLA: n8n cron, reaction-time = status age vs priority×state matrix; sets Flag + Due date, Slack/email + escalation |
| 18 | Integration → Integrated by n8n when all Code Review children merged — per-MR MR merged at stamped on the merge webhook, rolled up; cron reconciles |