# How the Claude Plugins Validation Action Works: A Deep Dive into the CI Pipeline

> Explore the Claude plugins validation action in anthropics/claude-plugins-community. Discover its three-layer CI pipeline for security, consistency, and schema compliance before merging.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: deep-dive
- Published: 2026-08-27

---

**The Claude plugins validation action is a composite GitHub Action in the `anthropics/claude-plugins-community` repository that enforces a three-layer defense-in-depth pipeline: change detection to scope validation, eleven hard-coded policy invariants (I1-I11) for security and consistency, and schema validation via the official `claude-code` CLI to ensure every plugin meets JSON Schema requirements before merging.**

Every plugin submitted to the Claude marketplace undergoes rigorous automated checks through the **Validate‑Plugins** GitHub Action. Located in the `anthropics/claude-plugins-community` repository, this composite action serves as the canonical CI gate, combining structural diffing, policy enforcement, and schema validation to prevent malformed or unsafe plugins from reaching production.

## Three-Layer Validation Architecture

The validation action implements a defense-in-depth strategy through three distinct layers:

- **Change Detection** – Identifies which marketplace entries, external plugins, and in-repo plugin folders were modified by the PR, ensuring validation effort is scoped only to relevant changes.
- **Policy Invariants** – Enforces eleven hard-coded "I-rules" (I1-I11) that validate naming conventions, sorting, description limits, URL safety, SHA presence, and hidden Unicode detection via [`11-validate-invariants.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/11-validate-invariants.sh).
- **Schema Validation** – Uses the official `claude-code` CLI (`@anthropic-ai/claude-code`) to execute `claude plugin validate` against the assembled [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) and individual plugin sources.

All layers are orchestrated by the composite action definition in [`.github/actions/validate-plugins/action.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/validate-plugins/action.yml) and invoked from [`.github/workflows/validate-plugins.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/validate-plugins.yml).

## Step-by-Step Execution Flow

### Environment Setup and CLI Installation

The action begins by configuring the runner environment. It installs **jq**, Node.js 20, and the `claude-code` CLI package. The CLI installation includes a resilient retry loop (lines 77-89 in [`action.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/action.yml)) to ensure the native binary is present even on flaky runners, guaranteeing that schema validation tools are available before proceeding.

### Change Detection (00-detect-changes.sh)

The script computes three JSON arrays representing modified artifacts:

- `entries` – Changed marketplace plugin names
- `external` – Changed entries where `source` is an external repository object
- `folders` – Changed in-repo plugin directories

Outputs are written to `$VALIDATE_TMP/changes.json` and exported as GitHub step outputs (`changed-entries`, `changed-external`, `changed-folders`). If the base ref cannot be fetched, the script gracefully degrades by treating **all** entries as changed, ensuring no validation gaps occur.

### Policy Invariant Enforcement (11-validate-invariants.sh)

This layer loads the assembled marketplace from `$VALIDATE_TMP/marketplace.json` and validates against eleven invariants:

- **I1-I2** – Enforce alphabetical order and uniqueness of plugin names
- **I3-I11** – Validate description length constraints, detect hidden Unicode characters, enforce name shape requirements, verify source URL safety protocols, check SHA completeness, validate vendored-source existence, and ensure shell-character safety

Invariants can be configured as **WARN** (via the `warn-invariants` input) or **ERROR**. When `scope-errors-to-changed` is set to `true`, any per-entry error on a plugin *not* touched by the PR is automatically downgraded to a warning, preventing legacy defects from blocking unrelated changes.

### CLI Schema Validation of Marketplace (20-validate-cli-marketplace.sh)

The action executes `claude plugin validate $MARKETPLACE_PATH` using the installed CLI. Schema violations are reported as GitHub annotation errors. With the `fail-on-warnings` input set, warnings can be escalated to hard failures.

### External Plugin Validation (30-validate-cli-external.sh)

For each changed external entry, the action:

1. Clones the referenced repository (respecting the `allowed-hosts` whitelist)
2. Checks out the specific commit identified by `source.sha`
3. Runs `claude plugin validate` against the repository's [`plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/plugin.json)

A configurable per-plugin timeout (`external-timeout-secs`) guards against hung clone operations on slow or unresponsive hosts.

### Local Folder and Auxiliary Validation

- **Local plugins ([`40-validate-cli-local.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/40-validate-cli-local.sh))** – Executes `claude plugin validate` on every changed in-repo plugin folder containing a [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json) file.
- **Auxiliary files ([`41-validate-aux-files.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/41-validate-aux-files.sh))** – Validates additional JSON manifests shipped with some plugins to ensure syntactic correctness.

### Reporting and Outputs (90-report.sh)

The final aggregation step emits a markdown summary to the path specified by `report-path` and sets the action output `result` to either **pass** or **fail`. All intermediate outputs—including `changed-entries`, `changed-external`, and `changed-folders`—are available to downstream workflow jobs for artifact upload or additional gating logic.

## Configuration and Key Inputs

The workflow invokes the action with several security-critical inputs:

```yaml
- uses: ./.github/actions/validate-plugins
  with:
    marketplace-path: .claude-plugin/marketplace.json
    skip-local-folders: "true"
    scope-errors-to-changed: "true"
    warn-invariants: "false"
    fail-on-warnings: "false"
    external-timeout-secs: "300"
    allowed-hosts: "github.com,gitlab.com"

```

- **`marketplace-path`** – Location of the assembled marketplace JSON
- **`skip-local-folders`** – Disables validation of in-repo plugins (useful for repositories hosting only external entries)
- **`scope-errors-to-changed`** – Downgrades errors on untouched plugins to warnings, preventing unrelated legacy issues from blocking PRs
- **`external-timeout-secs`** – Per-plugin clone timeout for external repositories
- **`allowed-hosts`** – Comma-separated whitelist of approved git hosts for external plugin sources

## Example Workflow Configuration

To implement the validation action in your repository workflow:

```yaml
name: Validate Plugins
on:
  pull_request:
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'
  push:
    branches: [main]
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for change detection

      - uses: ./.github/actions/validate-plugins
        id: validate
        with:
          marketplace-path: .claude-plugin/marketplace.json
          scope-errors-to-changed: "true"
          skip-local-folders: "true"

      - name: Upload Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: validation-report
          path: ${{ steps.validate.outputs.report-path }}

```

For local development testing outside of CI, you can run the CLI validator manually:

```bash

# Install the official CLI

npm install -g @anthropic-ai/claude-code@latest

# Validate the marketplace file

claude plugin validate .claude-plugin/marketplace.json

# Validate a single vendored plugin

claude plugin validate path/to/plugin/.claude-plugin

```

## Summary

- The **Validate-Plugins** action provides three layers of security: change detection, policy invariants (I1-I11), and JSON Schema validation via the official CLI.
- Validation scripts are located in `.github/actions/validate-plugins/scripts/` and cover everything from Unicode detection to external repository cloning with SHA verification.
- Key configuration options like `scope-errors-to-changed` and `warn-invariants` allow maintainers to balance strictness with pragmatic legacy support.
- All validation outputs—including `changed-entries` arrays and markdown reports—are exposed as GitHub Action outputs for downstream automation.

## Frequently Asked Questions

### What are the I1-I11 policy invariants?

The policy invariants are hard-coded security and consistency checks enforced by [`11-validate-invariants.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/11-validate-invariants.sh). **I1-I2** ensure alphabetical ordering and name uniqueness, while **I3-I11** enforce description length limits, detect hidden Unicode characters, validate URL safety protocols, verify SHA completeness for external sources, and check for shell-safe characters. These invariants prevent common marketplace pollution vectors and ensure consistent metadata quality.

### How does the action handle validation of external plugins from other repositories?

The action clones external repositories referenced in the marketplace JSON, validates the clone against an `allowed-hosts` whitelist, checks out the exact commit specified by the `source.sha` field, and runs `claude plugin validate` against the remote [`plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/plugin.json). A configurable `external-timeout-secs` parameter prevents CI hangs from slow git operations.

### Can the validation action be run locally for testing?

While the composite action itself requires a GitHub Actions runner, the underlying validation logic can be executed locally by installing the `@anthropic-ai/claude-code` CLI via npm and running `claude plugin validate` against your marketplace file or individual plugin directories. This allows developers to catch schema errors before submitting pull requests.

### What happens when a legacy validation error exists in an unchanged plugin?

When `scope-errors-to-changed` is set to `true`, the action automatically downgrades any ERROR-level finding on plugins not modified by the current PR to WARNING status. This prevents historical validation failures from blocking unrelated contributions while still surfacing issues in the CI logs for eventual remediation.