# How the Claude Plugins Community Close-External-PRs Workflow Works

> Understand the close-external-prs workflow in anthropics/claude-plugins-community. Learn why it closes external PRs and exempts github-actions[bot] for automated updates.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-09-02

---

**The close-external-prs workflow in the anthropics/claude-plugins-community repository automatically closes pull requests from external contributors because the repo is a read-only mirror, while exempting `github-actions[bot]` to allow automated bump PRs from the internal Bump-Plugin-SHAs workflow to remain open for review.**

The **close-external-prs** workflow is a critical guardrail for this GitHub repository. Since the `anthropics/claude-plugins-community` repo serves as a public mirror of Anthropic's internal plugin marketplace, direct community contributions via pull request cannot be merged. The workflow enforces this policy gracefully—closing external PRs with an explanatory comment—while carefully preserving the repository's own internal automation.

## Trigger Mechanism: Why pull_request_target Matters

The workflow runs on **`pull_request_target`** events of types **opened** or **reopened**. This choice is deliberate and security-critical.

- **Base-repo context**: Unlike standard `pull_request` events, `pull_request_target` executes in the context of the *target* repository, not the fork.
- **Write token availability**: This allows the workflow to post comments and close PRs even when they originate from untrusted forks, using a `GITHUB_TOKEN` with write permissions.

The workflow grants **minimal permissions**—only `pull-requests: write` and `issues: write`—sufficient for commenting and state changes without exposing broader repository access.

## Core Logic in close-external-prs.yml

The implementation in [[`.github/workflows/close-external-prs.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/close-external-prs.yml)](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/close-external-prs.yml) follows a three-tier validation flow:

### Step 1: Extract the PR Author

The script reads `context.payload.pull_request.user.login` to identify who created the pull request. This value is authoritative—it reflects the authenticated GitHub user, not spoofable metadata like branch names.

### Step 2: Exempt github-actions[bot]

The critical exemption appears early in the script:

```js
if (author === 'github-actions[bot]') {
  console.log(`${author} is this repo's bump automation — allowing PR`);
  return;
}

```

This check prevents the workflow from closing PRs created by the **Bump-Plugin-SHAs** workflow defined in [[`.github/workflows/bump-plugin-shas.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/bump-plugin-shas.yml)](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/bump-plugin-shas.yml). That nightly automation updates plugin SHAs and creates PRs using the default `GITHUB_TOKEN`, which attributes authorship to `github-actions[bot]`.

**Why author verification beats branch name filtering**: A malicious fork could name a branch `bump/plugin-shas` to impersonate internal automation. The `user.login` property cannot be forged by external actors, making it the only reliable signal for identifying trusted automation.

### Step 3: Check Collaborator Permissions

For non-bot authors, the workflow queries permission levels via `repos.getCollaboratorPermissionLevel`:

```js
const { data: permission } = await octokit.rest.repos.getCollaboratorPermissionLevel({
  owner: context.repo.owner,
  repo: context.repo.repo,
  username: author
});

if (permission.permission === 'admin' || permission.permission === 'write') {
  console.log(`${author} has ${permission.permission} access — allowing PR`);
  return;
}

```

Users with **admin** or **write** permissions are internal collaborators whose PRs should proceed.

### Step 4: Close External PRs

Any remaining PRs—those from external contributors without elevated permissions—trigger the closure sequence:

1. **Post explanatory comment** via `issues.createComment`, directing users to the official submission portal at `clau.de/plugin-directory-submission`
2. **Close the PR** via `pulls.update` with `state: 'closed'`

## The Automation Ecosystem: How Bump-Plugin-SHAs Creates Exempt PRs

Understanding the exemption requires examining the complementary automation in [[`.github/workflows/bump-plugin-shas.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/bump-plugin-shas.yml)](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/bump-plugin-shas.yml) and its supporting action at [`.github/actions/bump-plugin-shas`](https://github.com/anthropics/claude-plugins-community/tree/main/.github/actions/bump-plugin-shas).

This workflow runs on a schedule to:
- Detect updated plugin versions from Anthropic's internal registry
- Generate commits with updated SHA references
- Create pull requests using the default `GITHUB_TOKEN`

The resulting PRs appear as:

```bash

# Commit attribution when using GITHUB_TOKEN

Author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

```

Without the author exemption in close-external-prs.yml, every nightly maintenance PR would be immediately closed, breaking the repository's sync mechanism.

## Security Design: Defending Against Evasion

The workflow's architecture reflects careful threat modeling:

| Attack Vector | Mitigation |
|-------------|-----------|
| Branch name spoofing (`bump/*`) | Author verification, not branch pattern matching |
| Fork impersonating internal user | `pull_request_target` runs in base repo with verified actor identities |
| Token privilege escalation | Minimal `permissions` declaration; no `contents` or `actions` scopes |
| Comment/closure API abuse | Explicit error handling and logging for observability |

## Practical Example: External vs. Internal PR Handling

### External contributor PR (auto-closed)

When a community member opens a PR from a fork:

```yaml

# Triggered by: pull_request_target with types: [opened, reopened]

# Author detected: some-external-user

# Permission check: returns 'read' or 'none'

# Result: comment posted, PR closed

```

The contributor receives:

```

Thanks for the PR! This repo is a read-only mirror — its contents are synced
nightly from Anthropic's internal review pipeline, so direct pull requests are
closed automatically.

To submit a plugin to the community marketplace, use clau.de/plugin-directory-submission.

```

### Internal bump PR (preserved)

When the scheduled Bump-Plugin-SHAs workflow runs:

```yaml

# Triggered by: schedule (cron)

# Creates PR with: GITHUB_TOKEN

# Author detected: github-actions[bot]

# Result: early return, PR remains open

```

The bump PR proceeds through normal review and merge workflow.

## Summary

- **The close-external-prs workflow uses `pull_request_target`** to safely handle PRs from forks with write-level permissions
- **`github-actions[bot]` is exempted** to prevent automatic closure of nightly SHA bump PRs created by the repository's own automation
- **Author verification (`user.login`)** provides a non-spoofable signal for identifying trusted automation, unlike branch names or commit messages
- **Permission level checks** allow legitimate maintainers with write/admin access to submit PRs
- **All external contributors receive actionable guidance** for the correct submission channel instead of silent rejection

## Frequently Asked Questions

### Why does the workflow use `pull_request_target` instead of `pull_request`?

The `pull_request_target` event runs workflows in the context of the *base* repository with access to secrets and write tokens, even when triggered by forks. This is necessary because the workflow must close PRs and post comments, which requires write permissions that `pull_request` events from forks cannot safely receive. The trade-off requires careful code review since the workflow executes untrusted PR code in a privileged context.

### Could an attacker bypass the `github-actions[bot]` exemption by naming their account similarly?

No. GitHub strictly controls the `github-actions[bot]` identity. Only workflows running with the default `GITHUB_TOKEN` can create commits and PRs attributed to this bot. External users cannot register accounts with brackets or impersonate this system identity—the `user.login` value is cryptographically verified by GitHub's authentication system.

### What happens if the Bump-Plugin-SHAs workflow uses a custom token instead of `GITHUB_TOKEN`?

If the bump workflow used a Personal Access Token (PAT) or GitHub App token, the PR author would reflect that token's owner rather than `github-actions[bot]`. The close-external-prs exemption would need updating to include that new actor, or the bump workflow would need to retain the default token for PR creation specifically. The current design intentionally uses `GITHUB_TOKEN` to simplify identity verification.

### Where is the official plugin submission process documented for users whose PRs are closed?

The workflow's closure comment directs contributors to `clau.de/plugin-directory-submission`. This external portal represents Anthropic's internal review pipeline—the same system that feeds the nightly sync into this read-only mirror. The comment template is defined in the workflow's JavaScript execution context within [[`.github/workflows/close-external-prs.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/close-external-prs.yml)](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/close-external-prs.yml).