Tools: GitLab 18.11 + Duo Agent Platform: CI Expert, Agentic SAST Auto-Resolution, Custom Flow YAML, and Credits Guardrails for AI-Native DevSecOps - 2025 Update

Tools: GitLab 18.11 + Duo Agent Platform: CI Expert, Agentic SAST Auto-Resolution, Custom Flow YAML, and Credits Guardrails for AI-Native DevSecOps - 2025 Update

1. Why GitLab 18.11 Is the Inflection Point — Q1 2026 Duo Agent Platform Trajectory

2. Duo Agent Platform Architecture — AI Gateway, Workflow Service, Knowledge Graph, Runner

2.1 AI Gateway — One Entry for Auth, Routing, and Metering

2.2 Workflow Service — Home of Custom Flow v1

2.4 GitLab Runner 18.11 — Where Remote Flows Actually Run

3. CI Expert Agent — From an Empty .gitlab-ci.yml to a Working Pipeline

3.1 Invocation and Output

3.2 Pipeline Failure Triage

4. Agentic SAST Vulnerability Resolution — Patch MRs Open the Moment a Scan Closes

4.1 The Workflow

4.2 Language Coverage and Triggers

4.3 Troubleshooting — "The MR was created but the diff is empty"

5. Custom Flow YAML Schema — How to Codify Your Own Workflow

5.1 Custom Flow Trigger Mapping

7. Model Policy — Sonnet 4.6 Default and Mistral for Self-Hosting

7.1 Model Selection Cheatsheet

8. 18.11 Migration Checklist — Forced PostgreSQL 17 and MR Pipeline Inputs

8.1 MR Pipeline Inputs Customization

8.2 Operational Checklist

9. Conclusion — "AI-Native DevSecOps" Is Now the GitLab Default Surface On April 16, 2026, GitLab shipped 18.11 and dropped three new agents onto the Duo Agent Platform that went GA back in January. The new lineup is the CI Expert Agent (Beta) that writes a working .gitlab-ci.yml from an empty repository, the Data Analyst Agent (GA) that answers natural-language queries via GLQL, and Agentic SAST Vulnerability Resolution (GA) that ships a ready-to-review merge request the moment a SAST scan finishes. At the same time the default model for Agentic Chat moved from Claude Haiku 4.5 to Claude Sonnet 4.6 on Vertex AI, the self-hosted LLM list gained Mistral AI, and — critically for ops teams — GitLab introduced subscription-level and per-user GitLab Credits caps to stop runaway spend. This post is the ManoIT DevSecOps team's consolidated reference: the four-tier Duo Agent Platform architecture, the Custom Flow v1 YAML schema, the CI/CD and SAST automation patterns, the Credits cost model, and the migration checklist (including the forced PostgreSQL 17 upgrade). The Q1 2026 storyline of GitLab Duo can be summarized in one line: "the AI plugin became the platform." 18.8 (2026-01-15) promoted the Duo Agent Platform to GA on Premium and Ultimate, shipping the Planner Agent, Security Analyst Agent, and the MCP Client all at once. 18.9 (2026-02-19) made self-hosted LLMs first-class. 18.10 introduced GitLab Credits as the usage-based billing currency in beta. 18.11 then converged the trajectory: it pushed CI Expert into Beta on every tier, promoted Agentic SAST to GA so a fix MR appears the instant a scan completes, and capped credit consumption at both the subscription and the user level. The architectural takeaway is that the agents are platform-native. External copilots see only the code in the IDE; Duo agents see the code, issues, MRs, pipelines, vulnerabilities, and runner logs of the same project at the same time. That is why the CI Expert can pick the right cache pattern instead of pasting a generic template — it actually inspects the repository. The 18.11 runtime decomposes into four well-defined components. Understanding their responsibilities aligns the self-hosted deployment, observability, and pricing model in one shot. Every Duo call passes through the AI Gateway. It validates the GitLab user token, routes to the configured model provider, and meters GitLab Credits. 18.11 adds two enforceable guardrails. A subscription-level usage cap suspends Duo Agent Platform access for the entire subscription as soon as on-demand credits cross the configured threshold. A per-user usage cap suspends only the offender, leaving everyone else unaffected. When both caps are configured, whichever cap is hit first wins. The Duo Workflow Service consumes YAML definitions written against the Flow Registry v1 specification with the fields version, environment, components, prompts, routers, and flow. In Custom Flow definitions, environment accepts only ambient (the chat and chat-partial values are restricted), the model field inside a prompts entry is not supported (the model is determined by group/instance settings), and v1 top-level name, description, and product_group are also restricted. The 18.11 headline addition is tool option and parameter overrides directly in the flow definition, which lets you pin tool behaviour and enforce guardrails per flow regardless of LLM defaults. The Knowledge Graph is a synced graph index over code, issues, MRs, pipelines, commits, and vulnerabilities. External copilots that depend on stale embeddings often answer with last week's context; Duo pulls fresh state from the Knowledge Graph on every call. That is why the CI Expert nails build/test patterns and the Agentic SAST agent can trace a vulnerable function through its callers. Remote Flows execute as pipeline jobs on a GitLab Runner; the Rails backend triggers them with start_workflow = true. 18.11 ships two ops-level improvements. First, a concrete helper image with bundled dependencies reduces external image pulls at job startup. Second, the job router feature flag is now read from the runner config file instead of an environment variable, which removes an entire class of incidents where a leaked or overridden env var broke routing silently. Bug fixes in the same release land CONCURRENT_PROJECT_ID collisions, the after_script ordering when pre_build_script fails, pipeline hangs on cache operations, and the silent fallback to job payload credentials when the DOCKER_AUTH_CONFIG credential helper binary is missing. CI Expert Agent shipped in 18.11 in Beta on every tier (Free, Premium, Ultimate) and on GitLab.com, Self-Managed, Dedicated, and Dedicated for Government. The premise is straightforward: starting from an empty repository or empty .gitlab-ci.yml, the agent inspects the codebase, asks a few guided questions about your build and test process, and emits a reviewable pipeline. It does not paste a template — it identifies language, framework, test runner, and cacheable directories from the actual code. You invoke CI Expert from the VS Code extension or the Web UI's Agentic Chat: A typical first-cut output for a Node.js + Helm project looks like this: Each decision comes with a plain-language rationale. For example: "We use npm ci --prefer-offline because package-lock.json exists, and we set it as the cache key so dependencies only reinstall when it changes." That rationale lands in the MR description, which materially reduces reviewer friction. CI Expert is not just for greenfield projects — it triages broken pipelines. It reads the failed job log, classifies issues like cache misses, missing tests, or image pull failures, and proposes a patch. Agentic SAST Vulnerability Resolution debuted as a Beta in 18.9 and is GA in 18.11 on Ultimate. The principle is "scan ends → next step happens immediately." When the SAST scan on the main branch completes, false-positive detection runs first; the surviving Critical/High findings are handed to the agent, which uses multi-shot reasoning to traverse the surrounding code and opens a fix MR plus a verification pipeline automatically. The auto-fix is most reliable for languages directly supported by GitLab Advanced SAST: C, C++, C#, Go, Java, JavaScript, Python, Ruby, and TypeScript. Other languages fall back to the Semgrep-based analyzer with reduced fix confidence. You can also manually trigger the agent for any SAST vulnerability from its detail page. Symptom: the vulnerability page shows "Resolution generated" but the MR diff is empty.

Cause: the agent's confidence assessment fell below threshold. 18.11 will not force a low-quality patch; it leaves an "Insufficient context" reason and a recommended next step (e.g., add the calling functions) in the MR description. Fix: confirm the Knowledge Graph index for the codebase is current, and re-trigger from the vulnerability detail page if needed. For platform engineering teams, the most attractive 18.11 capability is tool option overrides combined with MCP integration. Below is a simplified version of the "security review assistant" Custom Flow that the ManoIT security team uses to pull in Confluence security guidelines as additional context. Two things matter here. First, tool_overrides (new in 18.11) pins tool call parameters so the LLM cannot drift from the contract — guardrails travel with the flow. Second, MCP tools (e.g., mcp.confluence.search) are first-class citizens alongside built-in GitLab tools. MCP configuration lives in a separate file gated by Code Owners approval. A Custom Flow can react to three trigger types, and each delivers a different shape of context:goal. Knowing this prevents flows from behaving inconsistently across triggers. Duo Agent Platform's cost model standardizes on GitLab Credits: 1 credit = $1 on-demand list price, billed monthly in arrears. 18.10 introduced automated code reviews at a flat $0.25 per MR, regardless of size. 18.11 added the two enforcement guardrails operations teams had been asking for. Both caps can run simultaneously, and whichever is hit first applies. Caps reset automatically each billing period. ManoIT's operational guidance: Two lines describe 18.11's model policy. Agentic Chat's default model moved from Claude Haiku 4.5 to Claude Sonnet 4.6 hosted on Vertex AI, and Mistral AI joined the supported self-hosted LLM list. Sonnet 4.6 reasons better but uses a higher GitLab Credit multiplier than Haiku. Existing explicit choices are preserved. Last but not least, the migration checklist the ManoIT operations team wrote up while moving to 18.11. The most consequential change is the forced PostgreSQL 17 path: 19.0's minimum supported version becomes PostgreSQL 17. Instances not running PostgreSQL Cluster will be auto-upgraded to PostgreSQL 17 during the 18.11 upgrade. PostgreSQL Cluster users (and anyone who opts out of the auto-upgrade) must move to PostgreSQL 17 manually before 19.0. 18.11 lets you override spec:inputs values per merge request pipeline run, so you can rerun the same pipeline against a different environment without code changes. By May 2026, GitLab 18.11 makes two things explicit. First, Duo Agent Platform is no longer an option — it is the default surface. The moment CI Expert reaches every tier, "AI writes your pipeline" stops being marketing and becomes the new-project baseline experience. Second, operations finally has hard guardrails. Subscription and per-user caps are not advisory — they cut access the instant the threshold is hit. ManoIT's roadmap for the next quarter splits into three workstreams. First, enable Agentic SAST GA to compress patch lead time for SOC 2 / ISO reporting into single-digit days. Second, standardize a code-review and release-notes automation pipeline using Custom Flows + MCP to bind Confluence, Jira, and Slack as live context. Third, layer OpenTelemetry-derived metrics on top of the GitLab Credits FinOps dashboard so cost, quality, and lead time live on the same screen. 18.11 is the first GitLab release that makes all three of those workstreams feasible in production. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# Web UI — Agentic Chat /ci-expert > Automate the build and test for this project, and deploy to Kubernetes on push to main. # VS Code (Duo extension) Cmd+Shift+P → "GitLab Duo: Generate CI Pipeline" # Web UI — Agentic Chat /ci-expert > Automate the build and test for this project, and deploy to Kubernetes on push to main. # VS Code (Duo extension) Cmd+Shift+P → "GitLab Duo: Generate CI Pipeline" # Web UI — Agentic Chat /ci-expert > Automate the build and test for this project, and deploy to Kubernetes on push to main. # VS Code (Duo extension) Cmd+Shift+P → "GitLab Duo: Generate CI Pipeline" # .gitlab-ci.yml — first draft from CI Expert stages: - -weight: 500;">install - test - build - deploy default: image: node:20-bookworm cache: key: files: - package-lock.json paths: - node_modules/ -weight: 500;">install: stage: -weight: 500;">install script: - -weight: 500;">npm ci --prefer-offline artifacts: paths: - node_modules/ expire_in: 1 hour unit-test: stage: test needs: ["-weight: 500;">install"] script: - -weight: 500;">npm run test:unit -- --reporter=junit artifacts: when: always reports: junit: junit.xml build-image: stage: build needs: ["unit-test"] image: -weight: 500;">docker:25 services: - -weight: 500;">docker:25-dind script: - -weight: 500;">docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - -weight: 500;">docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA rules: - if: $CI_COMMIT_BRANCH == "main" deploy-prod: stage: deploy needs: ["build-image"] image: alpine/helm:3.16.2 script: - helm -weight: 500;">upgrade ---weight: 500;">install web ./chart --set image.tag=$CI_COMMIT_SHA --namespace prod --atomic --wait environment: name: production url: https://app.example.com rules: - if: $CI_COMMIT_BRANCH == "main" when: manual # .gitlab-ci.yml — first draft from CI Expert stages: - -weight: 500;">install - test - build - deploy default: image: node:20-bookworm cache: key: files: - package-lock.json paths: - node_modules/ -weight: 500;">install: stage: -weight: 500;">install script: - -weight: 500;">npm ci --prefer-offline artifacts: paths: - node_modules/ expire_in: 1 hour unit-test: stage: test needs: ["-weight: 500;">install"] script: - -weight: 500;">npm run test:unit -- --reporter=junit artifacts: when: always reports: junit: junit.xml build-image: stage: build needs: ["unit-test"] image: -weight: 500;">docker:25 services: - -weight: 500;">docker:25-dind script: - -weight: 500;">docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - -weight: 500;">docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA rules: - if: $CI_COMMIT_BRANCH == "main" deploy-prod: stage: deploy needs: ["build-image"] image: alpine/helm:3.16.2 script: - helm -weight: 500;">upgrade ---weight: 500;">install web ./chart --set image.tag=$CI_COMMIT_SHA --namespace prod --atomic --wait environment: name: production url: https://app.example.com rules: - if: $CI_COMMIT_BRANCH == "main" when: manual # .gitlab-ci.yml — first draft from CI Expert stages: - -weight: 500;">install - test - build - deploy default: image: node:20-bookworm cache: key: files: - package-lock.json paths: - node_modules/ -weight: 500;">install: stage: -weight: 500;">install script: - -weight: 500;">npm ci --prefer-offline artifacts: paths: - node_modules/ expire_in: 1 hour unit-test: stage: test needs: ["-weight: 500;">install"] script: - -weight: 500;">npm run test:unit -- --reporter=junit artifacts: when: always reports: junit: junit.xml build-image: stage: build needs: ["unit-test"] image: -weight: 500;">docker:25 services: - -weight: 500;">docker:25-dind script: - -weight: 500;">docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - -weight: 500;">docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA rules: - if: $CI_COMMIT_BRANCH == "main" deploy-prod: stage: deploy needs: ["build-image"] image: alpine/helm:3.16.2 script: - helm -weight: 500;">upgrade ---weight: 500;">install web ./chart --set image.tag=$CI_COMMIT_SHA --namespace prod --atomic --wait environment: name: production url: https://app.example.com rules: - if: $CI_COMMIT_BRANCH == "main" when: manual # Trigger directly from a failed job gitlab-duo ci-expert debug --job-id=42 --project=acme/web # Sample output Diagnosis: - Cache miss: package-lock.json modified since last successful run. - Suggested: pin Node 20.18.1, add --frozen-lockfile guard. Patch (preview): - -weight: 500;">install: - script: - - -weight: 500;">npm ci --prefer-offline + - -weight: 500;">npm ci --prefer-offline --frozen-lockfile Apply? [y/N] # Trigger directly from a failed job gitlab-duo ci-expert debug --job-id=42 --project=acme/web # Sample output Diagnosis: - Cache miss: package-lock.json modified since last successful run. - Suggested: pin Node 20.18.1, add --frozen-lockfile guard. Patch (preview): - -weight: 500;">install: - script: - - -weight: 500;">npm ci --prefer-offline + - -weight: 500;">npm ci --prefer-offline --frozen-lockfile Apply? [y/N] # Trigger directly from a failed job gitlab-duo ci-expert debug --job-id=42 --project=acme/web # Sample output Diagnosis: - Cache miss: package-lock.json modified since last successful run. - Suggested: pin Node 20.18.1, add --frozen-lockfile guard. Patch (preview): - -weight: 500;">install: - script: - - -weight: 500;">npm ci --prefer-offline + - -weight: 500;">npm ci --prefer-offline --frozen-lockfile Apply? [y/N] # .gitlab-ci.yml — Agentic SAST integration (Ultimate) include: - template: Jobs/SAST.gitlab-ci.yml variables: # Auto-fix after SAST (default true in 18.11) SAST_DUO_AUTO_RESOLVE: "true" # Extend to medium severity if desired SAST_DUO_AUTO_RESOLVE_SEVERITY: "critical,high,medium" # Branch the auto-fix MR targets SAST_DUO_AUTO_RESOLVE_TARGET_REF: "$CI_DEFAULT_BRANCH" # Extra SAST verification for auto-fix MRs sast-verify: stage: test rules: - if: $CI_MERGE_REQUEST_LABELS =~ /duo:auto-fix/ script: - echo "Re-running SAST on auto-generated fix MR" # .gitlab-ci.yml — Agentic SAST integration (Ultimate) include: - template: Jobs/SAST.gitlab-ci.yml variables: # Auto-fix after SAST (default true in 18.11) SAST_DUO_AUTO_RESOLVE: "true" # Extend to medium severity if desired SAST_DUO_AUTO_RESOLVE_SEVERITY: "critical,high,medium" # Branch the auto-fix MR targets SAST_DUO_AUTO_RESOLVE_TARGET_REF: "$CI_DEFAULT_BRANCH" # Extra SAST verification for auto-fix MRs sast-verify: stage: test rules: - if: $CI_MERGE_REQUEST_LABELS =~ /duo:auto-fix/ script: - echo "Re-running SAST on auto-generated fix MR" # .gitlab-ci.yml — Agentic SAST integration (Ultimate) include: - template: Jobs/SAST.gitlab-ci.yml variables: # Auto-fix after SAST (default true in 18.11) SAST_DUO_AUTO_RESOLVE: "true" # Extend to medium severity if desired SAST_DUO_AUTO_RESOLVE_SEVERITY: "critical,high,medium" # Branch the auto-fix MR targets SAST_DUO_AUTO_RESOLVE_TARGET_REF: "$CI_DEFAULT_BRANCH" # Extra SAST verification for auto-fix MRs sast-verify: stage: test rules: - if: $CI_MERGE_REQUEST_LABELS =~ /duo:auto-fix/ script: - echo "Re-running SAST on auto-generated fix MR" # .gitlab/duo/flows/security_review.yml version: "1" environment: ambient # custom flows allow only "ambient" components: - name: collect_context type: AgentComponent prompt_id: collect_context_prompt inputs: - from: "context:project_id" as: "project_id" - from: "context:goal" as: "mr_iid" # 18.11: tool overrides pin parameters regardless of LLM defaults tool_overrides: - tool: "gitlab.read_merge_request" params: include_diff: true include_pipelines: true max_files: 50 - tool: "mcp.confluence.search" params: space: "SECURITY" max_results: 10 - name: review type: AgentComponent prompt_id: security_review_prompt inputs: - from: "components.collect_context.output" as: "evidence" prompts: - id: collect_context_prompt template: | Read MR {{ mr_iid }} in project {{ project_id }}. Pull related security guidelines from Confluence SECURITY space. Output structured evidence. - id: security_review_prompt template: | Given evidence: {{ evidence }} Produce a security review with: - Risk classification (low/medium/high/critical) - Specific code locations of concern - Suggested mitigations referencing internal policies routers: - from: collect_context to: review flow: - collect_context - review # .gitlab/duo/flows/security_review.yml version: "1" environment: ambient # custom flows allow only "ambient" components: - name: collect_context type: AgentComponent prompt_id: collect_context_prompt inputs: - from: "context:project_id" as: "project_id" - from: "context:goal" as: "mr_iid" # 18.11: tool overrides pin parameters regardless of LLM defaults tool_overrides: - tool: "gitlab.read_merge_request" params: include_diff: true include_pipelines: true max_files: 50 - tool: "mcp.confluence.search" params: space: "SECURITY" max_results: 10 - name: review type: AgentComponent prompt_id: security_review_prompt inputs: - from: "components.collect_context.output" as: "evidence" prompts: - id: collect_context_prompt template: | Read MR {{ mr_iid }} in project {{ project_id }}. Pull related security guidelines from Confluence SECURITY space. Output structured evidence. - id: security_review_prompt template: | Given evidence: {{ evidence }} Produce a security review with: - Risk classification (low/medium/high/critical) - Specific code locations of concern - Suggested mitigations referencing internal policies routers: - from: collect_context to: review flow: - collect_context - review # .gitlab/duo/flows/security_review.yml version: "1" environment: ambient # custom flows allow only "ambient" components: - name: collect_context type: AgentComponent prompt_id: collect_context_prompt inputs: - from: "context:project_id" as: "project_id" - from: "context:goal" as: "mr_iid" # 18.11: tool overrides pin parameters regardless of LLM defaults tool_overrides: - tool: "gitlab.read_merge_request" params: include_diff: true include_pipelines: true max_files: 50 - tool: "mcp.confluence.search" params: space: "SECURITY" max_results: 10 - name: review type: AgentComponent prompt_id: security_review_prompt inputs: - from: "components.collect_context.output" as: "evidence" prompts: - id: collect_context_prompt template: | Read MR {{ mr_iid }} in project {{ project_id }}. Pull related security guidelines from Confluence SECURITY space. Output structured evidence. - id: security_review_prompt template: | Given evidence: {{ evidence }} Produce a security review with: - Risk classification (low/medium/high/critical) - Specific code locations of concern - Suggested mitigations referencing internal policies routers: - from: collect_context to: review flow: - collect_context - review // .gitlab/duo/mcp.json — Code Owners approval required { "mcpServers": { "confluence": { "command": "npx", "args": ["-y", "@atlassian/mcp-confluence"], "env": { "CONFLUENCE_BASE_URL": "https://acme.atlassian.net/wiki", "CONFLUENCE_TOKEN": "$CONFLUENCE_API_TOKEN" } }, "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "$SLACK_DUO_BOT_TOKEN" } } } } // .gitlab/duo/mcp.json — Code Owners approval required { "mcpServers": { "confluence": { "command": "npx", "args": ["-y", "@atlassian/mcp-confluence"], "env": { "CONFLUENCE_BASE_URL": "https://acme.atlassian.net/wiki", "CONFLUENCE_TOKEN": "$CONFLUENCE_API_TOKEN" } }, "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "$SLACK_DUO_BOT_TOKEN" } } } } // .gitlab/duo/mcp.json — Code Owners approval required { "mcpServers": { "confluence": { "command": "npx", "args": ["-y", "@atlassian/mcp-confluence"], "env": { "CONFLUENCE_BASE_URL": "https://acme.atlassian.net/wiki", "CONFLUENCE_TOKEN": "$CONFLUENCE_API_TOKEN" } }, "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "$SLACK_DUO_BOT_TOKEN" } } } } # config/gitlab.rb — adding Mistral to self-hosted models (new in 18.11) gitlab_rails['duo_self_hosted_models'] = [ { 'name' => 'mistral-large-latest', 'provider' => 'mistral', 'endpoint' => 'https://api.mistral.ai/v1', 'api_key_env' => 'MISTRAL_API_KEY', 'use_for' => ['code_completion', 'agentic_chat'] }, { 'name' => 'claude-sonnet-4-6', 'provider' => 'vertex_ai', 'endpoint' => 'https://us-central1-aiplatform.googleapis.com/v1', 'project_id' => 'acme-prod-ai', 'use_for' => ['agentic_chat', 'agentic_sast'] } ] # config/gitlab.rb — adding Mistral to self-hosted models (new in 18.11) gitlab_rails['duo_self_hosted_models'] = [ { 'name' => 'mistral-large-latest', 'provider' => 'mistral', 'endpoint' => 'https://api.mistral.ai/v1', 'api_key_env' => 'MISTRAL_API_KEY', 'use_for' => ['code_completion', 'agentic_chat'] }, { 'name' => 'claude-sonnet-4-6', 'provider' => 'vertex_ai', 'endpoint' => 'https://us-central1-aiplatform.googleapis.com/v1', 'project_id' => 'acme-prod-ai', 'use_for' => ['agentic_chat', 'agentic_sast'] } ] # config/gitlab.rb — adding Mistral to self-hosted models (new in 18.11) gitlab_rails['duo_self_hosted_models'] = [ { 'name' => 'mistral-large-latest', 'provider' => 'mistral', 'endpoint' => 'https://api.mistral.ai/v1', 'api_key_env' => 'MISTRAL_API_KEY', 'use_for' => ['code_completion', 'agentic_chat'] }, { 'name' => 'claude-sonnet-4-6', 'provider' => 'vertex_ai', 'endpoint' => 'https://us-central1-aiplatform.googleapis.com/v1', 'project_id' => 'acme-prod-ai', 'use_for' => ['agentic_chat', 'agentic_sast'] } ] # .gitlab-ci.yml — 18.11 MR pipeline inputs spec: inputs: target_env: default: "staging" options: ["staging", "uat", "prod"] canary_traffic: default: 10 type: number --- deploy: stage: deploy script: - helm -weight: 500;">upgrade web ./chart --set env=$[[ inputs.target_env ]] --set canaryTraffic=$[[ inputs.canary_traffic ]] rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" # .gitlab-ci.yml — 18.11 MR pipeline inputs spec: inputs: target_env: default: "staging" options: ["staging", "uat", "prod"] canary_traffic: default: 10 type: number --- deploy: stage: deploy script: - helm -weight: 500;">upgrade web ./chart --set env=$[[ inputs.target_env ]] --set canaryTraffic=$[[ inputs.canary_traffic ]] rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" # .gitlab-ci.yml — 18.11 MR pipeline inputs spec: inputs: target_env: default: "staging" options: ["staging", "uat", "prod"] canary_traffic: default: 10 type: number --- deploy: stage: deploy script: - helm -weight: 500;">upgrade web ./chart --set env=$[[ inputs.target_env ]] --set canaryTraffic=$[[ inputs.canary_traffic ]] rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - The SAST scanner finishes on the main branch and writes the vulnerability list. - SAST False Positive Detection runs to drop noisy findings. - The agent analyses each remaining Critical/High vulnerability in context. - If confidence is sufficient, an MR is created and a SAST scan re-runs against the proposed patch to confirm the fix. - The vulnerability detail page exposes a one-click "Apply resolution" button. - Set the per-user cap at ~1.5× the average developer's expected usage (e.g., 50 credits/month) to absorb the learning ramp without runaway. - Set the subscription cap conservatively in the first quarter (e.g., 80% of forecast) and tighten or relax it after two cycles of dashboard data. - Agentic SAST runs in the background, so budget separately for it depending on scan cadence (every push vs. nightly). - Backup and rollback plan — full PostgreSQL backup and WAL archive verified before the auto--weight: 500;">upgrade. - Runner -weight: 500;">upgrade — move runners to 18.11 to pick up the concrete helper image and the runner-config-driven job router. Remove any scripts that forced job router via env vars. - Agentic SAST policy — decide whether to -weight: 500;">enable the GA auto-fix and define the MR label workflow (e.g., duo:auto-fix). - Credits caps — configure both subscription and per-user caps. Compare actual consumption on the Credits dashboard for the first 7 days. - Model policy — assess Sonnet 4.6 default cost impact; route background calls (Agentic SAST etc.) to Haiku or Mistral. - Custom Flow audit — change environment from chat / chat-partial to ambient. Remove fields rejected by 18.11: model in prompts, response_schema_id, -weight: 500;">stop, top-level name, description, product_group. - MCP governance — .gitlab/duo/mcp.json requires Code Owners approval. Inject tokens via GitLab CI/CD variables or Vault.