Skip to main content
Version: 0.8.2

Task Lifecycle — Phase 3: Review & aggregate approval

Phase 3 of the task lifecycle, run by the jira-task-reviewer skill. Triggered from the parent's worktree (branch-derived key; no key argument) after every leaf executor has reported back and transitioned its issue to <STATUS_IN_REVIEW>.

The reviewer handles both single-step top-level issues (no sub-tasks) and multistep parents (with sub-tasks). For single-step, it reviews the one PR and posts a final report — no re-run needed (GitHub-for-Jira auto-transitions the issue to <STATUS_DONE> on merge). For multistep, it reviews each sub-task PR, finds or creates the aggregate parent PR once sub-tasks are merged, and reviews that too. The reviewer never merges anything — that remains the user's deliberate step. When a run approved anything, its last step (7) asks whether to move those issues to <STATUS_DONE> and transitions only what the user says yes to.

The diagram surfaces the two systems the reviewer drives as their own swimlanes — GIT (anything that mutates or reads repo/PR state: git fetch --prune, resolving the parent/base branches from the branch.<PARENT-BRANCH>.parentbranch git config that the assigner wrote in phase 1, the phase-check gh pr list, the per-PR 3a idempotency lookup, fetching PR diffs, gh pr review --comment --body-file with APPROVED — / CHANGES REQUESTED — body-prefix verdicts, and finding or creating the aggregate parent PR) and JIRA (anything that mutates issue state: fetching the parent + In-Review sub-tasks, climbing from a sub-task branch to its parent, each rejected sub-task → In Progress transition, multi-line comments on the reviewed issue after every verdict, the per-sub-task summary comment on the parent, every report comment posted on the parent, and the step-7 user-approved → Done transitions) — so the full interaction reads User ↔ Reviewer ↔ GIT ↔ JIRA left to right.

Sequence diagram

What the diagram shows

  • Two tracks, one skill — step 1 determines the track from fields.subtasks: empty → single-step (the PR set is the one parent PR), non-empty → multistep (the PR set is each In Review sub-task PR). Each track walks its own branches from the same phase check; the review-loop body (step 3, including its 3a idempotency check) is identical — only the PR set and the post-loop outcomes differ.
  • Participant routing — the reviewer orchestrates three parties. GIT owns repo/PR state: the opening fetch, resolving <PARENT-BRANCH> + <BASE_BRANCH> (the latter read from the parentbranch git config the assigner wrote in phase 1 — the phase-1 → phase-3 thread), the phase-check gh pr list, the per-PR 3a idempotency lookup, fetching PR diffs, the verdict comment (gh pr review --comment --body-file), and finding or creating the aggregate parent PR. JIRA owns issue state: fetching the parent + sub-tasks (filtering to <STATUS_IN_REVIEW>), climbing from a sub-task branch to its parent, each rejected sub-task → In Progress transition with its findings comment, and the summary/report comments posted on the parent after every review.
  • Parent via climb (sub-task branches climb up) — the reviewer derives the key from the current branch (feature/-slug or hotfix/-slug) and jira.sh fetches the issue. If the issue is a Subtask, step 1 climbs to its parent via fields.parent.key and continues from there — the opt branch key is a Subtask block right after the initial Jira fetch. A top-level issue with no sub-tasks follows the single-step track.
  • Phase check first, and track-aware — an explicit GIT gh pr list whose return dispatches the top-level branches. On the single-step track it has three outcomes: no PR → report that the executor hasn't opened one yet and exit (nothing to review); an open PR → the step-3 review loop; a merged PR → the S-MERGED report and exit. On the multistep track: no parent PR → a full sub-task review pass (step 2 onward); an open parent PR → skip straight to the step-5 aggregate review; a merged parent PR → the M-FULLY-COMPLETE report and exit. Every merged-state exit posts the step-6 report only — GitHub-for-Jira already handled <STATUS_DONE>, so there is no wrap-up to take and step 7 has nothing to offer.
  • The reviewer carries its own Jira identity on every call — there is no login step to run first. jira.sh / jira.ps1 authenticates per-request as --role reviewer, picking that role's email:token pair out of jira-sdlc-tools.local.env for the one call, so every canonical report and every reject-path transition below is attributed to the reviewer's Jira account. Nothing is stored and no account is "active", which is why a reviewer run can overlap a phase-2 executor run without either one displacing the other's credentials. Note this is the reviewer's Jira identity; its GitHub identity is a separate thing, and the one that matters for the idempotency check below. See plugins/jira-sdlc/skills/_shared/project-config.md and plugins/jira-sdlc/skills/_shared/jira-api-reference.md §9.
  • Idempotent review (step 3a) — before every PR review — the single-step PR, each multistep sub-task PR, and the aggregate parent PR (5b) — the reviewer checks whether its own GitHub identity already left a verdict comment (matched on author + body prefix). A prior APPROVED — is terminal: it skips re-review and reports the PR as already approved/awaiting merge. A prior CHANGES REQUESTED — (with no later approval) triggers a fresh re-review of the pushed fixes. This is the 3a idempotency node drawn in each review branch.
  • Empty exits and flag-and-skip (step 2) — on the multistep first pass the reviewer first discovers each In Review sub-task's branch and open PR. A sub-task with no branch or no open PR is flagged and skipped, not reviewed. If zero sub-tasks have an open PR, the run reports that there is nothing to review and exits before the review loop.
  • Both verdicts go through --comment --body-file — in this plugin's default deployment the executor and reviewer share one gh account, and GitHub blocks an author from approving or requesting changes on their own PR. Both verdicts are recorded as review comments with the decision in their body prefix (APPROVED — … / CHANGES REQUESTED — …). The Jira transition to <STATUS_IN_PROGRESS> (reject path) is the actual workflow gate; the GitHub comment records findings and makes the verdict machine-detectable by the idempotency check (step 3a).
  • Single-step is one-and-done — on the single-step track, approval posts the final report immediately (S-APPROVED outcome, step 6), then step 7 offers to close the issue. Decline and GitHub-for-Jira auto-transitions it to <STATUS_DONE> when the user merges instead; either way no reviewer re-run is required. Only the S-CHANGES-REQUESTED outcome (reject) needs a re-run after fixes.
  • Single pass, no merge cascade — each In Review sub-task PR is reviewed in order, one at a time. The verdict happens immediately per-PR. There is no separate "batch merge" step; approved PRs are left for the human to merge manually, and rejected items keep the loop going so the full state is known. The only thing that stops the loop is running out of sub-task PRs.
  • Continue on rejection, don't stop — when a PR fails the review, the reviewer posts the CHANGES REQUESTED — verdict comment, transitions that issue back to <STATUS_IN_PROGRESS>, records it as blocked, and continues to the next sub-task. After every single PR (approved or rejected), a short summary comment is posted on the parent so the human can see progress in real time (intentional audit trail). The final report at the end (step 6) lists both approved and rejected items so the fix-and-re-run cycle is clear.
  • Parent PR: review, approve or reject, still never merge (multistep only) — once every sub-task PR is merged, the reviewer finds or creates the aggregate parent PR (GIT). If that PR is already CLOSED it stops and lets the user decide (5a) — it never reopens or recreates it. Otherwise it reviews the lighter aggregate diff (integration focus) and records a verdict on the parent PR via gh pr review --comment --body-file: approve → M-PARENT-READY (awaiting the human's manual merge), or request changes → M-PARENT-CHANGES-REQUESTED (integration file:line findings; fix on <PARENT-BRANCH> and re-run). Unlike a sub-task reject, the parent reject does not transition any Jira issue — the aggregate <PARENT-KEY> stays as-is. The reviewer never calls gh pr merge on the parent — merging into <BASE_BRANCH> is the human release decision. After the parent PR merges, no re-run is required; a re-run only reports the already-merged state (M-FULLY-COMPLETE).
  • No automatic status transitions — the reviewer never moves an issue to Done on its own. The only transition it performs unprompted is a rejected issue → In Progress, and only on a sub-task reject (3d) or a single-step reject (3d) — never on the multistep parent-PR reject (5b), where no executor will pick the parent branch up anyway.
  • Done is offered, never assumed (step 7) — the run's last step names every issue it approved and asks once whether to close them, moving only what the user confirms. It asks because an approval is not a merge: the reviewer never merges, so those PRs are still open, and on boards where Done means merged the card would be jumping ahead of the automation that really closes it. Boards that close at approval want the opposite, and nothing in the repo says which kind this project is. Declining (or a non-interactive run) leaves everything as-is and GitHub-for-Jira's merge automation closes the issues later. The already-merged exits (S-MERGED, M-FULLY-COMPLETE) skip the question — those issues are Done already. No extra Jira comment is posted either way: the step-6 report is still the run's single final comment.
  • Every terminal branch posts a JIRA report comment on the parent — per step 6, the report goes to chat and as a single Jira comment on <PARENT-KEY>: the single-step no-PR-yet exit, the single-step approve/reject (S-APPROVED / S-CHANGES-REQUESTED) and merged (S-MERGED) reports, the multistep nothing-to-review exit, the M-ALL-APPROVED and M-SOME-BLOCKED reports, the parent approve (M-PARENT-READY) and parent reject (M-PARENT-CHANGES-REQUESTED) reports, and the merged-state report (M-FULLY-COMPLETE). The one branch that stops without a step-6 report is the parent-PR-CLOSED case (5a) — it surfaces the state to the user in chat and waits for their decision.