# How Multiple Reviewers Assigned to a Single Pull Request Are Processed by the GitHub-Asana Integration

> Discover how the GitHub Asana integration handles multiple reviewers on a pull request. Each reviewer gets a unique Asana sub-task for independent tracking and management.

- Repository: [Keita Kitamura/github-asana-request-review-action](https://github.com/keitap/github-asana-request-review-action)
- Tags: how-to-guide
- Published: 2026-03-05

---

**When multiple reviewers are assigned to a single pull request, the GitHub-Asana integration creates a separate Asana sub-task for each individual reviewer, ensuring every review request is tracked independently.**

The `keitap/github-asana-request-review-action` repository automates synchronization between GitHub pull requests and Asana project tasks. When multiple reviewers are assigned to a single pull request, the action processes each reviewer sequentially rather than grouping them, generating distinct tracking sub-tasks that reflect each person's individual review responsibilities.

## Event Detection and Reviewer List Retrieval

The workflow triggers on `pull_request` events with the action type `review_requested`. In [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go), the `updateReviewers` function (lines 97-105) orchestrates the initial response by determining whether to use reviewers from the webhook payload or fetch them from GitHub's API:

```go
// handler.go:97-105
if hasRequestedReviewersFields {
    ghReviewers = pr.PullRequest.RequestedReviewers
} else {
    ghReviewers, err = getRequestedReviewers(h.gh,
        pr.GetRepo().GetOwner().GetLogin(),
        pr.GetRepo().GetName(),
        pr.GetNumber())
}

```

If the event payload lacks the `RequestedReviewers` field, the code calls `getRequestedReviewers` (defined in [`github.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/github.go)) to query the current reviewer list directly from GitHub's API.

## Processing Each Reviewer Individually

Once the reviewer list is obtained, the handler iterates through every `github.User` object to resolve Asana accounts and create tracking entries. This loop at lines 124-136 in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) ensures **no reviewer is skipped** and **no batching occurs**:

```go
// handler.go:115-136
reviewers = make([]*Account, len(ghReviewers))
for i, r := range ghReviewers {
    reviewers[i], err = h.fetchAccount(r.GetLogin())
    if err != nil {
        return err
    }
    log.Printf("reviewer: %s", reviewers[i])
}

for _, reviewer := range reviewers {
    if err := h.upsertReviewer(pr, requester, reviewer, taskID); err != nil {
        return err
    }
}

```

The `fetchAccount` function maps GitHub usernames to Asana user GIDs using the configuration defined in [`config.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go). Each resolved account is then passed individually to `upsertReviewer`.

## Sub-Task Creation and Update Logic

The `upsertReviewer` function (lines 49-78 in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go)) handles the actual Asana task manipulation for each reviewer separately:

```go
func (h *Handler) upsertReviewer(pr *github.PullRequestEvent, requester *Account, reviewer *Account, taskID string) error {
    // Lines 49-53: Skip unlinked accounts
    if reviewer.AsanaUserGID == "" {
        return nil
    }
    
    // Search for existing sub-task by reviewer name
    subtask, _ := FindSubtaskByName(h.ac, taskID, reviewer.Name)
    
    if subtask == nil {
        // Lines 63-71: Create new sub-task for this specific reviewer
        subtask, err = AddCodeReviewSubtask(h.ac, taskID, pr.GetNumber(),
            requester, reviewer, due, pr)
    } else {
        // Lines 73-78: Update existing sub-task
        err = UpdateCodeReviewSubtask(h.ac, subtask, requester, pr)
    }
    return err
}

```

**Key implementation details for multiple reviewers:**

- **Individual Tracking**: Each reviewer receives a dedicated sub-task under the parent Asana feature task, named specifically for that reviewer.
- **Due Date Assignment**: Every sub-task is automatically assigned a due date of the next business day, creating individual deadlines per reviewer.
- **Selective Processing**: If `reviewer.AsanaUserGID` is empty (indicating no mapping exists in [`config.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go)), that specific reviewer is ignored while processing continues for others.

## Handling Reviewer List Modifications

When reviewers are added or removed after the initial assignment, subsequent `review_requested` events trigger the same `updateReviewers` flow. The action re-fetches the complete reviewer list and reconciles the Asana sub-tasks:

- **New reviewers** trigger `AddCodeReviewSubtask` (lines 63-71) via `upsertReviewer`.
- **Existing reviewers** trigger `UpdateCodeReviewSubtask` (lines 73-78), refreshing metadata and PR links.
- **Removed reviewers** are handled implicitly through the absence of their names in the current reviewer list during the next sync cycle.

## Summary

- **Multiple reviewers assigned to a single pull request** generate **one Asana sub-task per reviewer**, not a single consolidated task.
- The `updateReviewers` function in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) (lines 97-136) iterates through the `RequestedReviewers` array, processing each `github.User` via `fetchAccount` and `upsertReviewer`.
- Reviewers lacking linked Asana accounts (`AsanaUserGID == ""`) are silently skipped at lines 49-53 while valid reviewers continue processing.
- The system uses `FindSubtaskByName` (from [`asana.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/asana.go)) to detect existing entries, then calls either `AddCodeReviewSubtask` or `UpdateCodeReviewSubtask` to maintain synchronization.
- Each generated sub-task contains reviewer-specific metadata, individual due dates, and direct links back to the GitHub pull request.

## Frequently Asked Questions

### Does the integration create one sub-task or multiple sub-tasks when several reviewers are assigned to a single pull request?

The integration creates **multiple sub-tasks**—one dedicated sub-task for each reviewer. According to the loop implementation in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) lines 124-136, the code processes every requested reviewer individually through `upsertReviewer`, resulting in separate Asana sub-tasks that track each person's review status independently.

### What happens if one assigned reviewer doesn't have an Asana account linked?

That specific reviewer is bypassed without affecting the others. In `upsertReviewer` ([`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go) lines 49-53), the code checks `if reviewer.AsanaUserGID == ""` and returns immediately, allowing the main loop to continue processing remaining reviewers who have valid Asana mappings defined in [`config.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/config.go).

### How does the action handle changes to the reviewer list after the initial assignment?

The action treats every `review_requested` event as a full synchronization. GitHub sends new webhooks when reviewers are added or removed, triggering `updateReviewers` to fetch the current list via `getRequestedReviewers` or the event payload. The handler then reconciles Asana sub-tasks by creating new ones for additions and updating existing ones for current reviewers.

### Is there a limit to how many reviewers can be processed on a single pull request?

There is **no coded limit** in [`handler.go`](https://github.com/keitap/github-asana-request-review-action/blob/main/handler.go). The implementation uses a standard `for` loop over the `ghReviewers` slice (lines 124-136), processing entries sequentially. Practical limits depend on GitHub API rate limits and Asana API throttling rather than constraints within the action logic itself.