> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainloop.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Using PR Policies as Control Gates

> Enforce pull request quality standards with Chainloop control gates in GitHub Actions and GitLab CI

## Overview

Chainloop provides a set of built-in policies that enforce pull request quality standards in your CI/CD pipeline. These policies evaluate PR metadata automatically gathered by the Chainloop CLI and can act as control gates to block workflows when requirements aren't met.

When used as control gates, PR policies help ensure:

* Code reviews are properly conducted
* PRs have meaningful descriptions and linked issues
* Security requirements like code owner approval are satisfied
* Conversation resolution and stale review dismissal rules are followed

## Available PR Policies

The following PR policies are available:

| Policy Name                           | Description                                                           |
| ------------------------------------- | --------------------------------------------------------------------- |
| `pr-review-required`                  | Ensures PRs require a minimum number of reviewers before merging      |
| `pr-code-owner-review-required`       | Verifies that code owners have reviewed and approved changes          |
| `pr-conversation-resolution-required` | Checks that all PR conversations are resolved before merging          |
| `pr-description-required`             | Ensures PRs have meaningful descriptions                              |
| `pr-stale-reviews-dismissed`          | Verifies that stale reviews are dismissed when new commits are pushed |
| `pr-user-story-linked`                | Ensures PRs reference a user story or issue for traceability          |
| `pr-bot-author-allowed`               | Verifies PRs/MRs are authored by approved bot accounts                |

All policies support both GitHub and GitLab.

## How It Works

1. **Automatic PR Detection**: The Chainloop CLI automatically detects when running in a pull request context and gathers PR metadata
2. **Material Collection**: PR information is collected as a `CHAINLOOP_PR_INFO` material type
3. **Policy Evaluation**: Attached PR policies evaluate the gathered metadata
4. **Control Gate**: If a policy is configured as a gate and fails, the workflow is blocked (exit code != 0)

## Quick Start

### Prerequisites

* Chainloop CLI configured in GitHub Actions or GitLab CI
* A workflow contract configured in Chainloop with PR policies attached that runs in PR or MR events

### Step 1: Create a Contract with PR Policies

Create a workflow contract that includes PR policies attached with the `gate: true` flag:

```yaml pr-quality-gate.contract.yaml theme={"dark"}
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: pr-quality-gate
  description: Enforce PR quality standards before merge
spec:
  policies:
    materials:
      # Require at least 2 reviewers
      - ref: pr-review-required
        gate: true
        with:
          min_reviewers: "2"
          branches: "main,release/*"

      # Require code owner approval
      - ref: pr-code-owner-review-required
        gate: true
        with:
          branches: "release/*"

      # Require meaningful PR description
      - ref: pr-description-required
        gate: true
        with:
          min_length: "50"
          allow_empty: "false"

      # Require linked issue or user story
      - ref: pr-user-story-linked
        gate: true
        with:
          patterns: "[A-Z]+-[0-9]+,#[0-9]+"
```

### Step 2: Set Up GitHub Workflow

Configure your GitHub Actions workflow to use the Chainloop CLI with runner context gathering:

```yaml .github/workflows/pr-quality-gate.yml theme={"dark"}
name: PR Quality Gate

on:
  pull_request:
    types: [opened, synchronize, reopened, edited]

jobs:
  pr-quality-check:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install Chainloop CLI EE
        run: |
          curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s

      - name: Initialize attestation (gathers PR info automatically)
        env:
          CHAINLOOP_TOKEN: ${{ secrets.CHAINLOOP_TOKEN }}
        run: |
          chainloop attestation init \
            --workflow pr-quality-gate \
            --project my-project

      - name: Push attestation
        env:
          CHAINLOOP_TOKEN: ${{ secrets.CHAINLOOP_TOKEN }}
        run: |
          # Push attestation - this will evaluate control gates
          chainloop attestation push
```

### Step 3: Configure Control Gate Enforcement

Enable control gate enforcement at the organization level or per-policy:

<Tabs>
  <Tab title="Organization-wide">
    ```bash theme={"dark"}
    chainloop org update --name my-org --block
    ```
  </Tab>

  <Tab title="Per-policy (in contract)">
    Already configured above using `gate: true` in the contract
  </Tab>
</Tabs>

## Policy Configuration Examples

### Example 1: Branch-specific Review Requirements

Require different numbers of reviewers based on target branch:

```yaml theme={"dark"}
policies:
  materials:
    # Stricter requirements for main branch
    - ref: pr-review-required
      gate: true
      with:
        min_reviewers: "3"
        branches: "main"

    # Lighter requirements for feature branches
    - ref: pr-review-required
      gate: true
      with:
        min_reviewers: "1"
        branches: "feature/*"
```

### Example 2: Enforce Conversation Resolution

Ensure all PR discussions are resolved before merge:

```yaml theme={"dark"}
policies:
  materials:
    - ref: pr-conversation-resolution-required
      gate: true
      with:
        branches: "main,release/*"
```

### Example 3: Require Meaningful PR Descriptions

Enforce PR descriptions with specific sections:

```yaml theme={"dark"}
policies:
  materials:
    - ref: pr-description-required
      gate: true
      with:
        min_length: "100"
        allow_empty: "false"
        required_sections: "summary,testing,motivation"
```

### Example 4: Ensure Stale Reviews Are Dismissed

Verify that old approvals are dismissed when new code is pushed:

```yaml theme={"dark"}
policies:
  materials:
    - ref: pr-stale-reviews-dismissed
      gate: true
      with:
        branches: "main"
```

## Gating Bot-Authored PRs

In addition to enforcing quality on human PRs, you can use Chainloop to verify and gate bot-authored PRs from tools like Dependabot or Renovate. This ensures only approved bots can create PRs in your repository and provides an attestation trail for automated dependency updates.

### How Bot Detection Works

<Tabs>
  <Tab title="GitHub">
    GitHub bot accounts have a platform-reserved `[bot]` suffix (e.g., `dependabot[bot]`, `renovate[bot]`) and a `type: "Bot"` field on the user object. The Chainloop CLI detects this automatically during `attestation init` when running in a pull request context.

    The approach uses two layers of defense:

    1. **Workflow trigger**: A GitHub Actions workflow triggers for any bot-authored PR using `github.event.pull_request.user.type == 'Bot'`
    2. **Policy gate**: The `pr-bot-author-allowed` policy restricts which specific bots are permitted. An unrecognized bot triggers the workflow but fails the policy gate.
  </Tab>

  <Tab title="GitLab">
    The Chainloop CLI calls `GET /api/v4/projects/:id/merge_requests/:iid` using the `CI_JOB_TOKEN` environment variable and reads the [`author.bot`](https://docs.gitlab.com/api/users.html) boolean field from the response. The result is mapped to the `Author.Type` field in the `CHAINLOOP_PR_INFO` material:

    | `author.bot` value | `Author.Type` | Meaning                                                                                                                                                                  |
    | ------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | `true`             | `"Bot"`       | Project/group access token bot users, [service accounts](https://docs.gitlab.com/api/group_service_accounts.html) (GitLab 16.1+, Premium/Ultimate), built-in GitLab bots |
    | `false`            | `"User"`      | Regular user account, including automation running under a personal access token                                                                                         |
    | API call fails     | `"unknown"`   | Falls back to `GITLAB_USER_LOGIN` environment variable                                                                                                                   |

    The same logic applies to merge request reviewers. For forked merge requests, the CLI prefers `CI_MERGE_REQUEST_PROJECT_PATH` over `CI_PROJECT_PATH` to resolve the correct project.

    <Warning>
      **Common pitfall with Renovate on GitLab**: If Renovate runs under a regular user account with a Personal Access Token (PAT) — which is the most common self-hosted setup — GitLab reports `author.bot = false`. The detected `Author.Type` will be `"User"` even though the merge requests are automated. See [recommended setups](#recommended-setup-for-renovate-on-gitlab) below.
    </Warning>

    **When `author.bot` is `true`:**

    * Bot users created by project or group access tokens
    * [Service accounts](https://docs.gitlab.com/api/group_service_accounts.html) (GitLab 16.1+, requires Premium or Ultimate)
    * Built-in GitLab bots (e.g., `alert_bot`, `support_bot`, `ghost`)

    **When `author.bot` is `false`:**

    * Regular user accounts — even if used exclusively for automation
    * This is the default for self-hosted Renovate configured with a standard user and PAT
  </Tab>
</Tabs>

### Recommended Setup for Renovate on GitLab

If you use self-hosted Renovate on GitLab, choose one of these approaches so the `pr-bot-author-allowed` policy works correctly:

<Tabs>
  <Tab title="Option A: GitLab Service Account (Preferred)">
    Provision the Renovate user as a [GitLab service account](https://docs.gitlab.com/api/group_service_accounts.html) (requires GitLab 16.1+, Premium or Ultimate). Service accounts have `author.bot = true` automatically, so the policy detects them as bots without additional configuration.

    1. Create a service account via the GitLab Groups API
    2. Generate a personal access token for the service account
    3. Configure Renovate to authenticate with that token
    4. No `allowed_bots` override is needed — the default policy configuration works
  </Tab>

  <Tab title="Option B: allowed_bots Override">
    If you cannot use a service account (e.g., GitLab Free tier or GitLab versions before 16.1), add the Renovate user's GitLab login name to the `allowed_bots` input:

    ```yaml theme={"dark"}
    policies:
      materials:
        - ref: pr-bot-author-allowed
          gate: true
          with:
            allowed_bots: "my-renovate-user"
    ```

    Replace `my-renovate-user` with the actual GitLab username that Renovate authenticates as. The policy matches on the author login name regardless of the `author.bot` value.

    <Note>
      The default allowed list (`dependabot[bot]`, `renovate[bot]`) uses GitHub-style names with the `[bot]` suffix. These will **not** match GitLab usernames, so you must specify the correct GitLab username explicitly.
    </Note>
  </Tab>
</Tabs>

### Contract

```yaml bot-pr-gate.contract.yaml theme={"dark"}
apiVersion: chainloop.dev/v1
kind: Contract
metadata:
  name: bot-pr-gate
  description: Verify and gate bot-authored PRs
spec:
  policies:
    materials:
      - ref: pr-bot-author-allowed
        gate: true
        with:
          allowed_bots: "dependabot[bot],github-actions[bot]"
```

### CI Configuration

<Tabs>
  <Tab title="GitHub Actions">
    ```yaml .github/workflows/bot-pr-attestation.yml theme={"dark"}
    name: Bot PR Attestation

    on:
      pull_request:
        types: [opened, synchronize, reopened, closed]
        branches:
          - main

    permissions:
      contents: read
      id-token: write
      pull-requests: read

    jobs:
      bot-attestation:
        if: ${{ github.event.pull_request.user.type == 'Bot' }}
        name: Attest Bot PR
        runs-on: ubuntu-latest
        env:
          CHAINLOOP_WORKFLOW_NAME: bot-pr-gate
          CHAINLOOP_PROJECT_NAME: my-project
        steps:
          - uses: actions/checkout@v4

          - name: Install Chainloop
            run: |
              curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s

          - name: Initialize Attestation
            run: |
              chainloop attestation init \
                --workflow $CHAINLOOP_WORKFLOW_NAME \
                --project $CHAINLOOP_PROJECT_NAME

          - name: Push Attestation
            if: ${{ success() }}
            run: chainloop attestation push

          - name: Mark attestation as failed
            if: ${{ failure() }}
            run: chainloop attestation reset

          - name: Mark attestation as cancelled
            if: ${{ cancelled() }}
            run: chainloop attestation reset --trigger cancellation
    ```
  </Tab>

  <Tab title="GitLab CI">
    ```yaml .gitlab-ci.yml theme={"dark"}
    bot-mr-attestation:
      stage: attestation
      rules:
        - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      variables:
        CHAINLOOP_WORKFLOW_NAME: bot-pr-gate
        CHAINLOOP_PROJECT_NAME: my-project
      script:
        - curl -sfL https://dl.chainloop.dev/cli/install.sh | bash -s
        - chainloop attestation init
            --workflow $CHAINLOOP_WORKFLOW_NAME
            --project $CHAINLOOP_PROJECT_NAME
        - chainloop attestation push
      after_script:
        - chainloop attestation reset
    ```

    <Note>
      GitLab CI does not have a built-in variable equivalent to GitHub's `github.event.pull_request.user.type` for filtering pipelines by author type at the trigger level. The `pr-bot-author-allowed` policy handles bot verification at the attestation layer instead, so the pipeline runs for all merge request events and the policy gate determines whether the author is an approved bot.
    </Note>
  </Tab>
</Tabs>

The `pr-bot-author-allowed` policy supports both string-based author names (e.g., `"dependabot[bot]"`) and structured author objects with explicit bot type detection, ensuring backwards compatibility with existing attestations.

### GitHub vs GitLab: Bot Detection Differences

| Aspect                  | GitHub                                               | GitLab                                                                                                          |
| ----------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Bot identifier          | `[bot]` suffix on username (e.g., `dependabot[bot]`) | [`author.bot`](https://docs.gitlab.com/api/users.html) boolean field on user object                             |
| API field               | `user.type == "Bot"`                                 | `author.bot == true`                                                                                            |
| Service automation      | GitHub Apps get the `[bot]` type automatically       | Requires a [service account](https://docs.gitlab.com/api/group_service_accounts.html) (Premium/Ultimate, 16.1+) |
| Regular user with PAT   | Detected as `"User"`                                 | Detected as `"User"`                                                                                            |
| CI-level bot filter     | `github.event.pull_request.user.type == 'Bot'`       | Not available — use the policy gate only                                                                        |
| Fallback on API failure | N/A (uses GitHub event payload)                      | `GITLAB_USER_LOGIN` env var, type = `"unknown"`                                                                 |

## Handling Policy Failures

When a PR policy control gate fails:

1. **The workflow is blocked**: `chainloop attestation push` returns a non-zero exit code
2. **The attestation is still recorded**: Chainloop tracks the non-compliant activity for audit purposes
3. **Developers see clear error messages**: Policy violations are displayed in the CLI output

Example failure output:

```bash theme={"dark"}
INF push completed
┌───────────────────────────┬─────────────────────────────────────────────────────────────────────────┐
│ Initialized At            │ 26 Dec 25 10:37 UTC                                                     │
├───────────────────────────┼─────────────────────────────────────────────────────────────────────────┤
│ Attestation ID            │ 1ecd79e7-2886-46e5-901b-a9a66482187c                                    │
│ Digest                    │ sha256:f78bd5337ae68ec58b14fffb0e5317f9ecf3d5ee61afd615a165b3243fbcb045 │
│ Organization              │ my-org                                                                  │
│ Name                      │ pr-quality-gate                                                         │
│ Project                   │ my-project                                                              │
│ Version                   │ v1.65.0+next (prerelease)                                               │
│ Contract                  │ pr-quality-gate (revision 1)                                            │
│ Policy violation strategy │ ADVISORY                                                                │
│ Policies                  │ ------                                                                  │
│                           │ pr-review-required (gate): Branch feature-1 in corp/my-repo has 1       │
│                           │    reviewers, minimum should be 2                                       │
└───────────────────────────┴─────────────────────────────────────────────────────────────────────────┘
ERR the policy "pr-review-required" is configured as a gate and has violations
```

### Bypassing Control Gates (Emergency Use)

In urgent situations (hotfixes, false positives), you can bypass control gates:

```bash theme={"dark"}
chainloop attestation push --exception-bypass-policy-check
```

**Important**: The bypass is recorded and exposed to the compliance team for review.

## Best Practices

1. **Start with non-blocking policies**: Test policies without `gate: true` first to ensure they work as expected
2. **Use branch-specific requirements**: Apply stricter rules to production branches (main, release/\*) and lighter rules to development branches
3. **Combine multiple policies**: Use multiple PR policies together for comprehensive quality checks
4. **Monitor policy violations**: Review Chainloop's UI regularly to identify patterns in policy failures
5. **Document bypass procedures**: Establish clear guidelines for when and how to use `--exception-bypass-policy-check`
6. **Keep policies updated**: Review and adjust policy parameters based on team feedback and compliance requirements

## Troubleshooting

### PR Info Not Being Gathered

**Problem**: The `CHAINLOOP_PR_INFO` material is not being collected automatically.

**Solutions**:

* Check that the workflow is triggered by a pull request event

### Policy Always Passes Despite Invalid PR State

**Problem**: Policy evaluates successfully even when PR doesn't meet requirements.

**Solutions**:

* Verify the policy's `branches` parameter includes the target branch
* Check that the PR metadata is being gathered correctly (inspect attestation in Chainloop UI)
* Review policy parameters in the contract's `with` section
* Ensure the policy is compatible with your CI/CD platform (GitHub vs GitLab)

### Control Gate Not Blocking Workflow

**Problem**: Workflow continues even when policy fails.

**Solutions**:

* Verify `gate: true` is set in the policy attachment
* Check organization-wide enforcement settings (`chainloop org describe`)
* Ensure you're not using `--exception-bypass-policy-check` flag
* Confirm the workflow doesn't have error handling that catches the non-zero exit code

## Additional Resources

* [Control Gates Concept](/concepts/control-gates)
* [Policies Concept](/concepts/policies)
* [Custom Policies Guide](/guides/custom-policies)
* [Branch Protection Policies Reference](/reference/branch-protection-policies)
