A red-team suite that runs only when a security engineer remembers to run it is a suite that will miss the regression that ships on a Friday.
Tier: Core · Workflow area: AI red-team program & tooling
The discipline that separates a real AI red-team program from a one-off audit is continuous evaluation: your Promptfoo (pb-41) and Garak (pb-40) checks run automatically on every pull request that touches the prompt, the model config, the retrieval sources, or the guardrails — and a regression fails the build. This playbook wires those evals into CI/CD so security regressions are caught at the same gate as failing unit tests, by the same engineers, before merge.
Make the suite run headless and exit non-zero on failure. Promptfoo already returns a non-zero exit code when assertions fail below threshold — that is what CI keys on.
# .github/workflows/ai-security-evals.yml
name: AI Security Evals
on:
pull_request:
paths:
- "prompts/**"
- "guardrails/**"
- "app/llm/**"
- "promptfooconfig.yaml"
jobs:
redteam:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<pinned-sha>
- uses: actions/setup-node@<pinned-sha>
- run: npm i -g promptfoo
- run: promptfoo eval -c promptfooconfig.yaml --no-progress-bar
env:
OPENAI_API_KEY: ${{ secrets.EVAL_MODEL_KEY }}
Gate on path filters, not every commit. Run the full red-team suite when the prompt, guardrails, model wiring, or retrieval config change. A docs-only PR should not pay for a full scan.
Set the failure threshold explicitly. Security assertions should be hard gates — 100% of the injection/leakage cases must pass. Use Promptfoo's --fail-on-error and per-assertion thresholds so a single reopened hole reds the check.
Run the heavy Garak scan on a schedule, not per-PR. Full probe scans are slow; put them on a nightly or pre-release schedule and treat a drop in pass rate as a blocking release-gate rather than a per-commit gate.
on:
schedule:
- cron: "0 2 * * *" # nightly full Garak baseline
Publish the report as a build artifact. Upload the Promptfoo/Garak output so reviewers can open the exact failing prompt and completion from the PR, without re-running anything.
Require the check to merge. Add the eval job to branch protection as a required status check. An eval that can be bypassed by clicking "merge anyway" is documentation, not a gate.
Keep per-PR evals fast (a focused deterministic suite under a couple of minutes) and push slow, LLM-graded, and full-scan work to scheduled runs — a security gate that adds ten minutes to every PR gets disabled within a week.
The gate is real only if a seeded regression turns the build red. Prove it end to end: open a throwaway PR that deliberately weakens a guardrail or reverts a system-prompt hardening line, push it, and confirm the CI eval job fails and blocks merge. Then confirm a clean PR passes. This "seed a regression, watch the pipeline fail" test is the single check that certifies the whole wiring — path filters that never trigger, a non-zero exit that CI ignores, or a status check that is not actually required will all silently let a regression through, and only a deliberate failing PR exposes them. Re-run this verification whenever you change the workflow file itself.
In a client's pipeline, keep the eval provider pointed at the client's own staging endpoint and store the eval-model credential in the client's CI secret store, never yours. Where the client prohibits sending prompts to an external eval model, run the suite against a local model inside their runners and use deterministic assertions so no third-party grader is needed. Deliver the workflow file and required-check configuration as a maintainable artifact the client's platform team owns, document the seeded-regression verification so they can re-prove the gate themselves, and ensure build artifacts (which contain real prompts and outputs) are retained only within the client's environment under their retention policy.