Skip to main content
A merge-request check that fails when the AgentGuardian AIVSS score drops below your floor — and surfaces the same scan three ways for reviewers: the SARIF report in GitLab’s security dashboard, a Code Quality widget that annotates findings inline on the MR diff, and a single sticky MR note that AgentGuardian creates once and updates in place on every push (no comment spam).

When to add this

  • The first time an LLM agent lands in main and needs a regression gate on merge requests.
  • On every release branch before tagging.
  • For any change that touches the agent’s system prompt, tool surface, memory layer, or framework graph.

Wire it up

Create .gitlab-ci.yml (or add an agentguardian job to your existing pipeline). The full copy-pasteable file lives at examples/ci/gitlab/.gitlab-ci.yml:
.gitlab-ci.yml
Four things to know:
  1. artifacts.when: always is mandatory — without it, a failed --fail-under would suppress the reports and leave reviewers without the annotations they need. The script captures the gate exit code (SCAN_EXIT) and re-raises it last so the reports + note are always published before the job goes red.
  2. artifacts.reports.sast feeds GitLab’s Security & Compliance → Vulnerability Report. The SARIF emitter produces a schema-valid file every time, so the report contract is satisfied without a converter step.
  3. artifacts.reports.codequality feeds the inline MR Code Quality widget. agent-guardian report --output gitlab emits one CodeClimate entry per finding, severity-mapped (critical/high/medium/lowblocker/critical/major/ minor/info) with a stable fingerprint so a recurring finding collapses to one row across MR updates instead of duplicating.
  4. rules scope the job to merge-request and main-branch pipelines so a feature-branch push doesn’t burn LLM budget.

The sticky MR note

agent-guardian comment --platform gitlab upserts a single note on the merge request: it lists the MR’s notes via the GitLab REST API, finds the one carrying AgentGuardian’s hidden marker, and PUTs it in place — or POSTs a fresh one if none exists. Push ten times and you still have exactly one always-current note, not ten. It reads its context from the standard GitLab CI variables (CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID) and authenticates with GITLAB_TOKEN (sent as PRIVATE-TOKEN), falling back to the auto-injected CI_JOB_TOKEN (JOB-TOKEN). Use a real GITLAB_TOKEN with the api scope — CI_JOB_TOKEN cannot write MR notes on most GitLab tiers. The --fail-under / --max-* flags mirror the scan gate so the note’s verdict matches the pipeline’s exit code. A rendered note looks like this:

AgentGuardian scan sc_01ABCDEF…

AIVSS 64/100 (Poor)  |  3 findings  |  $0.061  |  47.2s

Gate: FAILED

  • AIVSS 64 is below the floor of 70

Top 3 findings

Because the gate failed, the job exits non-zero and the merge is blocked; the note and the inline Code Quality annotations tell the reviewer exactly why without opening the pipeline log.

Pick a target

Replace my_app.graph:graph with the dotted reference to your real framework-native object. Supported --framework values: adk, autogen, crewai, langgraph, openai_agents, strands. For a hosted HTTP agent, swap the framework flags for:
and set AGENT_GUARDIAN_AUTH_BEARER from a masked CI variable.

Add the provider secret

In GitLab: Settings → CI/CD → Variables → Add variable. Add the key matching your --model choice — GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY. Mark it Masked and Protected if you only want main-branch pipelines to read it. For a free offline smoke check, use --model stub — but note that stub runs are non-authoritative (mode_authoritative=false) and always fail --fail-under regardless of the numeric score. See AIVSS score → mode_authoritative.

How to interpret the exit code

GitLab CI uses the same exit codes the GitHub workflow does. The job fails on anything non-zero; the SARIF is still uploaded because of artifacts.when: always. Full reference: CLI exit codes + Exit codes.

Tune the floor

Same progression as the GitHub Actions page:
  • First two weeks--fail-under 60. Catches catastrophic regressions, lets the team see what a real swarm finds.
  • Steady state--fail-under 70. Matches the WARNING/POOR boundary; rejects merges that introduce a medium-severity ASI01 / ASI02 finding.
  • Hardened release branch--fail-under 80. Matches the GOOD/WARNING boundary; only ships when the agent has no high-severity outstanding findings.
Band cutoffs: src/agent_guardian/models/severity.py.

Cap the spend

Pass --budget-usd so a runaway provider can never cost more than budgeted per pipeline. The swarm soft-stops new attack turns at 80 % of the cap and reserves the remainder for the report emission step.
On a gemini:gemini-2.5-flash --mode full run the typical cost is ~$0.06; 0.10 gives headroom + the soft-stop reserve.

Next step

Reports

Open the scan.sarif and the signed scan.json every job emits.

Fail builds on high risk

Add a finding gate on top of the score gate.

GitHub Actions

The same flow on GitHub, with Code Scanning annotations.

CLI reference

Every flag on agent-guardian scan.