# Channels - multi-repo dispatch

**Multi-repo dispatch:** When `state.projects[].length > 1`, the dispatcher delegates per-channel rendering to `~/.claude/lib/channels-multi-repo.sh`:

| Channel | Behavior | Adapter call |
|---------|----------|--------------|
| **PR description** | Posted to **every** target's PR. Primary's body lists extras as `Related: <url>`; each extra's body lists primary as `Part of: <url>`. | `channels-multi-repo.sh render-pr <state> <body> <repoName>` per target |
| **Jira comment** | Posted **once** (Jira ticket is shared by all repos). Body prepended with bulleted `* PR:` list  -  primary first, extras after. | `channels-multi-repo.sh render-jira <state> <body>` once |
| **Confluence page** | Posted **once** to the primary's component slug. `## Related PRs` heading block prepended. | `channels-multi-repo.sh render-conf <state> <body>` once |
| **Wiki** | Posted **once** to the primary repo's wiki, since wiki scope is `figma-config.wikiRepo` (single repo). | No adapter  -  body unchanged |
| **Board** | Posted **once**  -  board status is keyed off the GitHub Issue, not per-PR. Primary repo's issue is the target. | No adapter  -  single mutation |

**Target enumeration:** `channels-multi-repo.sh targets <state>` returns a JSON array `[{repoName, prUrl, prNumber, isPrimary, crossLinks}]` for the dispatch loop. Single-repo tasks return one entry with empty `crossLinks`  -  every render-* call falls through to the body unchanged, so the loop is uniform across single-repo and multi-repo paths.

**Logging hint:** `channels-multi-repo.sh summary <state>` produces a one-line autopilot log entry (`1 primary (foo) + 2 extras: <url> <url>` or `single-repo task`).
