# CI/CD Setup Guide - Multi-Provider Support

Complete guide for setting up MP Sentinel in your CI/CD pipeline with any AI provider.

## Quick Start

### GitHub Actions

1. **Choose your provider** (see examples below)
2. **Add API key to GitHub Secrets**
   - Go to: Repository → Settings → Secrets and variables → Actions
   - Click: "New repository secret"
   - Add your key (see provider-specific instructions)
3. **Create workflow file**: `.github/workflows/audit.yml`
4. **Commit and push**

### GitLab CI

1. **Choose your provider** (see examples below)
2. **Add API key to GitLab Variables**
   - Go to: Settings → CI/CD → Variables
   - Click: "Add variable"
   - Add your key with flags: ☑️ Protect variable, ☑️ Mask variable
3. **Create CI file**: `.gitlab-ci.yml`
4. **Commit and push**

---

## Provider-Specific Setup

### Option 0: xAI Grok (Extreme Reasoning)

**Best for:** Finding logical race conditions, exploitability analysis, and high-speed reasoning.

#### Get API Key
1. Visit: https://console.x.ai/
2. Log in with your X (Twitter) account.
3. Go to "API Keys" → "Create Key".
4. Copy your API key (starts with `xai-`).

#### GitHub Actions Setup

**Add Secret:**
- Name: `GROK_API_KEY`
- Value: Your API key

**Workflow file** (`.github/workflows/audit.yml`):
```yaml
name: MP Sentinel Code Guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npm run build
      - name: Run Audit with Grok
        env:
          AI_PROVIDER: grok
          AI_MODEL: grok-4-1-fast-reasoning
          GROK_API_KEY: ${{ secrets.GROK_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npx mp-sentinel --target-branch origin/${{ github.base_ref }}
```

---

## Environment Variables

All AI providers require an API key as an environment variable. Set it via your CI/CD platform's secrets manager.

When using `ANTHROPIC_BASE_URL` (e.g., with DeepSeek), set `AI_PROVIDER=anthropic`, the DeepSeek API key as `ANTHROPIC_API_KEY`, and `ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic`. The custom base URL bypasses the Anthropic model whitelist so you can use any model the endpoint supports.

---

### Option 1: Google Gemini (Free Tier)

**Best for:** Getting started, high-volume reviews, cost-conscious teams

#### Get API Key
1. Visit: https://aistudio.google.com/
2. Click "Get API key"
3. Create or select a project
4. Copy your API key

#### GitHub Actions Setup

**Add Secret:**
- Name: `GEMINI_API_KEY`
- Value: Your API key

**Workflow file** (`.github/workflows/audit.yml`):
```yaml
name: MP Sentinel Code Guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Run Audit
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TARGET_BRANCH: origin/${{ github.base_ref }}
        run: npx mp-sentinel --target-branch $TARGET_BRANCH
```

#### GitLab CI Setup

**Add Variable:**
- Key: `GEMINI_API_KEY`
- Value: Your API key
- Flags: ☑️ Protect, ☑️ Mask

**CI file** (`.gitlab-ci.yml`):
```yaml
image: node:24

stages:
  - audit

code_audit:
  stage: audit
  before_script:
    - npm ci
    - git fetch --unshallow 2>/dev/null || true
    - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  script:
    - npm run build
    - npx mp-sentinel indexing
    - npx mp-sentinel --target-branch origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  variables:
    GEMINI_API_KEY: $GEMINI_API_KEY
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
```

---

### Option 2: OpenAI GPT-5 (Best Accuracy)

**Best for:** Critical code reviews, complex refactoring, enterprise teams

#### Get API Key
1. Visit: https://platform.openai.com/api-keys
2. Click "Create new secret key"
3. Name it (e.g., "MP Sentinel")
4. Copy your API key (starts with `sk-`)

#### GitHub Actions Setup

**Add Secret:**
- Name: `OPENAI_API_KEY`
- Value: Your API key (sk-...)

**Workflow file** (`.github/workflows/audit.yml`):
```yaml
name: MP Sentinel Code Guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Run Audit with GPT-5
        env:
          AI_PROVIDER: openai
          AI_MODEL: gpt-5.2  # or gpt-5.2 for best coding
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TARGET_BRANCH: origin/${{ github.base_ref }}
        run: npx mp-sentinel --target-branch $TARGET_BRANCH
```

#### GitLab CI Setup

**Add Variable:**
- Key: `OPENAI_API_KEY`
- Value: Your API key (sk-...)
- Flags: ☑️ Protect, ☑️ Mask

**CI file** (`.gitlab-ci.yml`):
```yaml
image: node:24

stages:
  - audit

code_audit:
  stage: audit
  before_script:
    - npm ci
    - git fetch --unshallow 2>/dev/null || true
    - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  script:
    - npm run build
    - npx mp-sentinel indexing
    - npx mp-sentinel --target-branch origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  variables:
    AI_PROVIDER: openai
    AI_MODEL: gpt-5.2
    OPENAI_API_KEY: $OPENAI_API_KEY
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
```

---

### Option 3: Anthropic Claude (Best for Agents)

**Best for:** Autonomous reviews, long-running tasks, detailed analysis

#### Get API Key
1. Visit: https://console.anthropic.com/
2. Click "API Keys" → "Create Key"
3. Name it (e.g., "MP Sentinel")
4. Copy your API key (starts with `sk-ant-`)

#### GitHub Actions Setup

**Add Secret:**
- Name: `ANTHROPIC_API_KEY`
- Value: Your API key (sk-ant-...)

**Workflow file** (`.github/workflows/audit.yml`):
```yaml
name: MP Sentinel Code Guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Run Audit with Claude
        env:
          AI_PROVIDER: anthropic
          AI_MODEL: claude-sonnet-4-6  # or claude-opus-4-6
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TARGET_BRANCH: origin/${{ github.base_ref }}
        run: npx mp-sentinel --target-branch $TARGET_BRANCH
```

#### GitLab CI Setup

**Add Variable:**
- Key: `ANTHROPIC_API_KEY`
- Value: Your API key (sk-ant-...)
- Flags: ☑️ Protect, ☑️ Mask

**CI file** (`.gitlab-ci.yml`):
```yaml
image: node:24

stages:
  - audit

code_audit:
  stage: audit
  before_script:
    - npm ci
    - git fetch --unshallow 2>/dev/null || true
    - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  script:
    - npm run build
    - npx mp-sentinel indexing
    - npx mp-sentinel --target-branch origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  variables:
    AI_PROVIDER: anthropic
    AI_MODEL: claude-sonnet-4-6
    ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
```

---

### Option 4: OpenRouter (Multi-Model Gateway)

**Best for:** Accessing any model through a single API, flexible model selection, cost optimization across providers

#### Get API Key
1. Visit: https://openrouter.ai/keys
2. Sign up or log in
3. Click "Create Key"
4. Copy your API key (starts with `sk-or-`)

#### GitHub Actions Setup

**Add Secret:**
- Name: `OPENROUTER_API_KEY`
- Value: Your API key (sk-or-...)

**Workflow file** (`.github/workflows/audit.yml`):
```yaml
name: MP Sentinel Code Guard
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      - name: Run Audit with OpenRouter
        env:
          AI_PROVIDER: openrouter
          AI_MODEL: openai/gpt-5.2  # Slash-form: provider/model
          OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TARGET_BRANCH: origin/${{ github.base_ref }}
          # Optional: attribution headers
          OPENROUTER_SITE_URL: https://github.com/${{ github.repository }}
          OPENROUTER_APP_NAME: MP Sentinel
        run: npx mp-sentinel --target-branch $TARGET_BRANCH
```

#### GitLab CI Setup

**Add Variable:**
- Key: `OPENROUTER_API_KEY`
- Value: Your API key (sk-or-...)
- Flags: ☑️ Protect, ☑️ Mask

**CI file** (`.gitlab-ci.yml`):
```yaml
image: node:24

stages:
  - audit

code_audit:
  stage: audit
  before_script:
    - npm ci
    - git fetch --unshallow 2>/dev/null || true
    - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  script:
    - npm run build
    - npx mp-sentinel indexing
    - npx mp-sentinel --target-branch origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main}
  variables:
    AI_PROVIDER: openrouter
    AI_MODEL: openai/gpt-5.2
    OPENROUTER_API_KEY: $OPENROUTER_API_KEY
    # Optional: attribution headers
    OPENROUTER_SITE_URL: $CI_PROJECT_URL
    OPENROUTER_APP_NAME: MP Sentinel
  rules:
    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
```

**Supported models include:**
- `openai/gpt-5.2` — OpenAI GPT-5.2
- `openai/gpt-5.2` — OpenAI GPT-5.3 Codex
- `anthropic/claude-sonnet-4-6` — Claude Sonnet 4.6
- `anthropic/claude-opus-4-6` — Claude Opus 4.6
- `google/gemini-2.5-flash` — Gemini 2.5 Flash (free tier via OpenRouter)

> **Note:** OpenRouter also supports optional `OPENROUTER_SITE_URL` and `OPENROUTER_APP_NAME` env vars for attribution on the OpenRouter dashboard. These are recommended but not required.

---

## PR/MR Comment Posting: Runtime vs Config

The runtime source of truth for PR/MR comment posting is **CI environment variables**, not `.mp-sentinelrc.json`.

| Behavior | Runtime source (authoritative) | Config field (informational only) |
|----------|-------------------------------|-----------------------------------|
| GitHub PR comments | `GITHUB_ACTIONS` env + `GITHUB_TOKEN` env | `gitProvider: "github"` |
| GitLab MR comments | `GITLAB_CI` env + `CI_JOB_TOKEN` or `GITLAB_TOKEN` env | `gitProvider: "gitlab"`, `projectId` |
| Repository URL | `GITHUB_REPOSITORY` or `CI_PROJECT_URL` env | `repoUrl` |

The fields `gitProvider`, `repoUrl`, and `projectId` in `.mp-sentinelrc.json` are **documentation hints** — they record intent but are not consumed at runtime. The CLI detects the platform from standard CI environment variables and uses CI-provided tokens for authentication. Do not rely on config alone to enable PR/MR comments; ensure the required CI env vars are set.

---

## GitLab Inline MR Comments

When a review runs inside GitLab CI on a merge request, mp-sentinel posts each `CRITICAL` and `WARNING` finding as an **inline discussion** on the exact changed line (`INFO` findings are never posted inline). Each comment includes:

- **Why this matters** — the finding explanation.
- **Suggested fix** — the free-text recommendation (when present).
- **Evidence** — supporting detail (when present).
- A GitLab **code suggestion block** — rendered only when the model returns a structured `codeSuggestion` that passes safety checks (small, pure code, no nested code fences, not prose). Reviewers can apply it with one click:

  ````md
  ```suggestion
  replacement code
  ```
  ````

### How it posts

- The diff version SHAs (`base`/`head`/`start`) are fetched from the MR **versions API** first, falling back to CI env vars (`CI_COMMIT_SHA`, `CI_MERGE_REQUEST_DIFF_BASE_SHA`, `CI_MERGE_REQUEST_DIFF_START_SHA`).
- Inline positions use `position_type: "text"` with `new_path` + `new_line`.
- Existing **discussions and MR-level notes** are both read first; a finding with the same fingerprint **updates its note** (via the matching Discussions or Notes endpoint) instead of creating a duplicate, so reruns on the same commit are safe — including reruns where the prior post was a fallback note.
- If GitLab rejects an inline position (e.g. the line is not part of the MR diff), mp-sentinel posts an **MR-level fallback note** with an explicit `file:line` reference so the finding is never silently dropped.
- With `--format json|sarif|markdown`, stdout is reserved for the report — comment-posting progress and success messages are written to **stderr**, so piping the report stays clean.

### Token permissions

| Token | Header | Notes |
|-------|--------|-------|
| `GITLAB_TOKEN` (PAT/project token) | `PRIVATE-TOKEN` | Preferred. Needs `api` scope to read versions/discussions and post notes. |
| `CI_JOB_TOKEN` (default CI token) | `JOB-TOKEN` | Used when `GITLAB_TOKEN` is unset. Posting discussions may require enabling job-token MR permissions on the project. |

`GITLAB_TOKEN` takes precedence over `CI_JOB_TOKEN` when both are present. Add it under **Settings → CI/CD → Variables** — the UI variable is injected automatically, so don't hardcode the value in `.gitlab-ci.yml` (`GITLAB_TOKEN: $GITLAB_TOKEN` is redundant but harmless).

**Protected-variable caveat (common cause of `401 Unauthorized`).** A GitLab variable marked **Protected** is only exposed to pipelines running on *protected* branches/tags. Merge-request pipelines on regular (unprotected) branches will NOT receive it. When a protected `GITLAB_TOKEN` is absent from the pipeline, mp-sentinel falls back to `CI_JOB_TOKEN` — which often lacks permission to read/create/update MR discussions and notes — and the GitLab API returns `401 Unauthorized` (the same exposure rule also hides a protected AI API key). To review normal MR branches, either:

1. leave `GITLAB_TOKEN` **unprotected** (still mask it) so MR-branch pipelines receive it; or
2. restrict reviews to protected branches and enable *Settings → CI/CD → Variables → Allow merge request pipelines to access protected variables and runners*.

Either way, confirm the token carries the `api` scope so discussions/notes calls are authorized.

### Code suggestion behavior

Code suggestion blocks come **only** from the structured `codeSuggestion` field — free-text `suggestion` recommendations are never coerced into a patch. v1 supports **single-line replacements only**: the model is instructed to emit `codeSuggestion` only for a high-confidence, single-line fix that matches the file's style. The suggestion is dropped (the textual guidance remains) when it is empty, multi-line, oversized, fenced, or reads like prose. Multi-line / range suggestions are out of scope for now.

---

## Manual Rerun via Slash Command (GitHub)

You can re-trigger a review by commenting `/mp-sentinel review` on a pull request. The bot updates its existing inline comments instead of posting duplicates, so repeated reruns on the same commit are safe and non-spammy.

### How it works

- A separate `issue_comment` workflow listens for comments containing `/mp-sentinel review`.
- Only comments from trusted users (repository `OWNER`, `MEMBER`, or `COLLABORATOR`) trigger execution. Trusted commenter association is necessary but not sufficient — the PR must also originate from the same repository (fork PRs are skipped for safety, since they run untrusted code with repository secrets).
- The workflow fetches PR metadata via REST (no checkout needed — `gh` is on every runner), verifies the PR head is from the same repo, then checks out the PR head, runs `mp-sentinel indexing`, and runs the review against `--target-branch "origin/$BASE_REF"`.
- Existing MP Sentinel inline comments are updated by fingerprint rather than duplicated.

### Caching (`.mp-sentinel-cache`)

The workflow restores `.mp-sentinel-cache` so repeated slash-command reruns on the same PR commit usually skip AI calls when the prompt hash is unchanged. Cache keys use PR number + PR head SHA (not comment ID), so reruns on the same commit hit the cache regardless of which comment triggered them. The save step only runs on a cache miss, avoiding redundant uploads.

Note: `actions/checkout` defaults to `clean: true` (wipes untracked files), so the checkout happens _before_ cache restore. Split `restore`/`save` is used so the cache can still be saved when the review finds issues (which causes a non-zero exit).

### Workflow example

Add this alongside your existing `pull_request` workflow (e.g., `.github/workflows/audit-slash.yml`):

```yaml
name: MP Sentinel Slash Command
on:
  issue_comment:
    types: [created]

jobs:
  rerun:
    if: |
      github.event.issue.pull_request &&
      contains(github.event.comment.body, '/mp-sentinel review') &&
      (github.event.comment.author_association == 'OWNER' ||
       github.event.comment.author_association == 'MEMBER' ||
       github.event.comment.author_association == 'COLLABORATOR')
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Get PR metadata
        id: pr
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          PR_DATA=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}")
          HEAD_SHA=$(echo "$PR_DATA" | jq -r .head.sha)
          HEAD_REPO=$(echo "$PR_DATA" | jq -r .head.repo.full_name)
          BASE_REF=$(echo "$PR_DATA" | jq -r .base.ref)
          echo "head_sha=$HEAD_SHA" >> $GITHUB_OUTPUT
          echo "head_repo=$HEAD_REPO" >> $GITHUB_OUTPUT
          echo "base_ref=$BASE_REF" >> $GITHUB_OUTPUT

      - name: Skip fork PRs
        if: steps.pr.outputs.head_repo != github.repository
        run: |
          echo "Skipping slash-command rerun: PR is from a fork (${{ steps.pr.outputs.head_repo }})."
          echo "Only same-repository PRs are supported for security reasons."

      - uses: actions/checkout@v4
        if: steps.pr.outputs.head_repo == github.repository
        with:
          ref: ${{ steps.pr.outputs.head_sha }}
          persist-credentials: false
          fetch-depth: 0

      - uses: actions/setup-node@v4
        if: steps.pr.outputs.head_repo == github.repository
        with:
          node-version: '20'
          cache: 'npm'

      - if: steps.pr.outputs.head_repo == github.repository
        run: npm ci

      - if: steps.pr.outputs.head_repo == github.repository
        run: npm run build

      - name: Fetch base branch
        if: steps.pr.outputs.head_repo == github.repository
        env:
          BASE_REF: ${{ steps.pr.outputs.base_ref }}
        run: git fetch origin "$BASE_REF"

      - name: Restore mp-sentinel cache
        id: cache
        if: steps.pr.outputs.head_repo == github.repository
        uses: actions/cache/restore@v4
        with:
          path: .mp-sentinel-cache
          key: mp-sentinel-${{ github.event.issue.number }}-${{ steps.pr.outputs.head_sha }}
          restore-keys: |
            mp-sentinel-${{ github.event.issue.number }}-

      - name: Build source index
        if: steps.pr.outputs.head_repo == github.repository
        run: npx mp-sentinel indexing

      - name: Run mp-sentinel review
        id: review
        if: steps.pr.outputs.head_repo == github.repository
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_EVENT_PATH: ${{ github.event_path }}
          BASE_REF: ${{ steps.pr.outputs.base_ref }}
        run: |
          exit_code=0
          npx mp-sentinel --target-branch "origin/$BASE_REF" || exit_code=$?
          echo "exit_code=$exit_code" >> $GITHUB_OUTPUT

      - name: Save mp-sentinel cache
        if: steps.pr.outputs.head_repo == github.repository && steps.cache.outputs.cache-hit != 'true'
        uses: actions/cache/save@v4
        with:
          path: .mp-sentinel-cache
          key: ${{ steps.cache.outputs.cache-primary-key }}

      - name: Surface review failure
        if: steps.pr.outputs.head_repo == github.repository && steps.review.outputs.exit_code != '0'
        run: exit ${{ steps.review.outputs.exit_code }}
```

### Limitations

- **GitHub only.** GitLab MR comment rerun is not supported in this release.
- **One command only.** The only recognized command string is `/mp-sentinel review`.

---

## Advanced Configuration

### Fine-Tuning AI Behavior

Add these environment variables to customize AI responses:

```yaml
env:
  AI_TEMPERATURE: 0.2  # Lower = more focused (0.0-1.0)
  AI_MAX_TOKENS: 8192  # Maximum response length
```

### Custom Concurrency

Control how many files are audited in parallel:

```yaml
run: npx mp-sentinel --target-branch $TARGET_BRANCH --concurrency 10
```

### Multiple Providers (Hybrid Approach)

Use different providers for different scenarios:

```yaml
jobs:
  quick-audit:
    # Fast review with Gemini
    env:
      GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
    run: npx mp-sentinel
  
  deep-audit:
    # Detailed review with GPT-5.2 (only on main branch)
    if: github.base_ref == 'main'
    env:
      AI_PROVIDER: openai
      AI_MODEL: gpt-5.2
      OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    run: npx mp-sentinel
```

---

## Cost Optimization

### GitHub Actions

**Free tier:** 2,000 minutes/month for private repos

**Optimization tips:**
1. Use caching (already included in examples)
2. Use Gemini for routine reviews
3. Use GPT-5 only for critical branches
4. Set `allow_failure: true` to not block PRs

### GitLab CI

**Free tier:** 400 minutes/month for private repos

**Optimization tips:**
1. Use caching (already included in examples)
2. Use `rules` to limit when audits run
3. The audit runs as a blocking check (like GitHub). If you need non-blocking behavior during a trial period, add `allow_failure: true`.

### AI Provider Costs

Pricing changes frequently. Check each provider's pricing page for current rates. As a general guide, Google Gemini tends to be the most cost-effective, while OpenAI and Anthropic models are typically higher-priced. OpenRouter pricing varies by the underlying model selected.

---

## Troubleshooting

### "API key not found"

**GitHub Actions:**
```bash
# Check if secret is set
# Go to: Settings → Secrets → Actions
# Verify secret name matches workflow file
```

**GitLab CI:**
```bash
# Check if variable is set
# Go to: Settings → CI/CD → Variables
# Verify variable name matches .gitlab-ci.yml
```

### "Rate limit exceeded"

**Solution:** Reduce concurrency
```yaml
run: npx mp-sentinel --concurrency 3
```

### "Model not found"

**Solution:** Check model name spelling
```yaml
# Correct model names:
AI_MODEL: gemini-2.5-flash  # Gemini
AI_MODEL: grok-4-1-fast-reasoning  # xAI Grok
AI_MODEL: gpt-5.2            # OpenAI
AI_MODEL: claude-sonnet-4-6 # Claude
AI_MODEL: openai/gpt-5.2   # OpenRouter (slash-form: provider/model)
```

### Workflow not triggering

**GitHub Actions:**
- Check workflow file is in `.github/workflows/`
- Verify `on:` trigger matches your use case
- Check branch protection rules

**GitLab CI:**
- Check file is named `.gitlab-ci.yml`
- Verify `rules:` conditions
- Check CI/CD is enabled in project settings

---

## Example Files

Example workflow files are available in the [`examples/workflows/`](../examples/workflows/) directory:

### GitHub Actions
- [`examples/workflows/github/audit-openai.yml.example`](../examples/workflows/github/audit-openai.yml.example) — OpenAI
- [`examples/workflows/github/audit-claude.yml.example`](../examples/workflows/github/audit-claude.yml.example) — Claude

### GitLab CI
- [`examples/workflows/gitlab/.gitlab-ci-openai.yml.example`](../examples/workflows/gitlab/.gitlab-ci-openai.yml.example) — OpenAI
- [`examples/workflows/gitlab/.gitlab-ci-claude.yml.example`](../examples/workflows/gitlab/.gitlab-ci-claude.yml.example) — Claude

For Gemini, Grok, and OpenRouter setups, copy one of the provider examples above and swap the `AI_PROVIDER`, `AI_MODEL`, and API key env var as shown in the provider-specific sections earlier in this guide.

**To use an example:**
1. Copy the example file to the correct CI location (e.g. `.github/workflows/audit.yml` or `.gitlab-ci.yml`)
2. Update the provider, model, and API key secret/variable
3. Commit and push

---

## Security Best Practices

1. **Never commit API keys** to your repository
2. **Use secrets/variables** for all sensitive data
3. **Enable "Mask variable"** in GitLab CI
4. **Rotate keys regularly** (every 90 days)
5. **Use separate keys** for different environments
6. **Monitor API usage** in provider dashboards
7. **Set spending limits** in provider settings

---

## Next Steps

1. ✅ Choose your AI provider
2. ✅ Get API key
3. ✅ Add to CI/CD secrets/variables
4. ✅ Create workflow/CI file
5. ✅ Test with a pull request
6. ✅ Monitor results and costs
7. ✅ Optimize based on needs

## Support

- 📖 [Full Documentation](../README.md)
- 🔧 [Provider Comparison](./PROVIDER_COMPARISON.md)
- 🚀 [Quick Start](./QUICK_START.md)
- 🐛 [Report Issues](https://github.com/peter-minhnn/mp-sentinel/issues)
