Add the risk assessment to the replacement MR overview

This adds a risk assessment panel to the new merge request overview. It shows the risk tier, the confidence, the rationale, and the signals behind the score, using GitLab's real risk classification data. The card shows the shared severity meter and the same risk headline as the merge widget up front, with the rationale and the signals collapsed until expanded. The panel only appears when a project actually has risk classification available - otherwise it shows nothing.

The labels and milestone MR (!257511 (merged)) has merged, so this now targets master. The conversations MRs are stacked on this one: !257832 (merged), then !257879 (merged), then !257871 (merged), then !257872 (merged).

Collapsed by default: the meter and the headline show, the details wait behind the chevron: mr257492-risk-meter-collapsed

Expanded: the rationale and the signals behind the score: mr257492-risk-meter-expanded

Expanded, for an assessment from before the latest push, with low confidence because two signals could not be measured: mr257492-risk-meter-stale

Detailed context for AI agents

Context

The replacement merge request Overview tab is being built as a chain of merge requests behind the mr_ai_overview feature flag (type wip), plus a per-user cookie toggle in the MR overflow menu. Merged so far:

!257511 (merged) added labels and milestone. It merged on 2026-09-25, and this MR now targets master. Stacked on this MR: !257832 (merged) (conversations card), then !257879 (merged) (filters, roles and file links), then !257871 (merged) (thread actions), then !257872 (merged) (code snippets and suggestions).

The original design for the overview, in the scaffold at !233471 (closed), had a "Risk:" line inside an "AI review summary" card. It was computed from simple guesses - conflicts, a failing pipeline, a large change - not real classification. It was dropped when !252481 (merged) was split out, because the MergeRequest.riskAssessment field existed but its score and tier always resolved to null at the time.

Master now has real risk classification: the backend landed in !253762 (merged), and the merge widget already has an extension that shows it. This MR brings that same risk information into the overview.

What this adds

A new component, ee/app/assets/javascripts/merge_requests/ai_overview/components/risk_panel.vue, mounted from app.vue in the main column directly under the merge readiness panel, above the description panel.

The panel runs its own GraphQL query rather than joining the overview's main query. It reuses the merge widget's query file, ee/app/assets/javascripts/vue_merge_request_widget/widgets/risk_classification/graphql/risk_assessment.query.graphql (operation getMergeRequestRiskAssessment), and reads projectPath and iid from the overview's existing provide. Keeping it out of the main query is deliberate: riskAssessment is an experimental field, and an error on it should not take down the rest of the overview.

It also reuses the widget's constants (status values) and its riskHeadline() helper from .../risk_classification/constants.js, the shared SeverityMeter component from ee/app/assets/javascripts/vue_shared/components/severity_meter/severity_meter.vue, and the shared ViewSessionButton component from ee/ai/shared/widgets/view_session_button.vue.

It does not reuse the widget's RiskRationale, RiskSignalBreakdown, or RiskStaleWarning components. Their headings and margins are sized for the merge widget, and inside a card they came out larger than the card title, with dead space left at the bottom. The panel renders the same content with more compact markup, using exactly the same translated strings. No widget code was changed.

Behavior by state

  • While loading: renders nothing.
  • The riskAssessment field is null (the project is not Ultimate, the risk classification flow is not available to it, or no assessment exists yet): renders nothing.
  • PENDING with no result yet: headline "Risk assessment: Pending", meter empty.
  • QUEUED with no result yet: headline "Risk assessment: Queued", meter empty.
  • QUEUED with a previous result (a refresh in progress): the previous headline and meter (filled to the previous tier) stay on screen with "Updating…" appended, and the previous confidence and details stay until the new score replaces them. Same rule as the merge widget.
  • FAILED: headline "Risk assessment: Unavailable", the meter is empty, and any previous result is hidden, because a failed run leaves nothing to trust. This is a deliberate divergence from the merge widget, which still fills its meter from the stale tier on a failed run.
  • COMPLETE: headline reads something like "Risk assessment: High with medium confidence", the meter filled to the tier (Low / Medium / High / Critical risk), then a staleness warning if new commits were pushed after the assessment, the rationale text, and a "What contributed to this score" list of signals with their detail lines. When confidence is LOW and some missing signals have labels, it adds a line saying the picture is partial and names the signals that could not be measured. Missing signals with no label (claims) are left out of that sentence, matching the widget.
  • The meter's accessible label reads "Risk level: risk" when filled, or "Risk level not available" when empty, via the existing RiskAssessment|Risk level: %{level} string.
  • A "View session" button appears when the assessment has a Duo session id. The button itself only shows when Duo Chat is available to the user.
  • If the query itself fails, the headline reads "Risk assessment: Not available" and the card shows the red line "Failed to load the risk assessment" below it, and the rest of the overview is unaffected.

The card's details (the staleness warning, the rationale, and the signal list) are collapsed by default. The header row, with the meter and the headline, always shows. A chevron button at the end of the header, the same animated chevron and tertiary icon button pattern crud_component uses, shows and hides the details. Its tooltip and label read "Show details" / "Hide details" (existing strings), and it carries aria-expanded and aria-controls. The details use v-show, so the element aria-controls points at always exists. There is no toggle when there are no details to show, or when the query failed.

Gating and data availability

There is no new feature flag. The panel lives inside the overview, so it is already behind mr_ai_overview and the per-user toggle.

The underlying data only exists where project.risk_classification_dap_available? is true: the duo_mr_risk_classification flag (wip), the foundational flows being enabled, the risk classification flow being enabled for the project, the Duo workflow stage check, and an Ultimate licence.

No new translated strings were added. Everything is reused from the existing RiskAssessment| namespace (renamed from RiskClassification| on master), so locale/gitlab.pot is unchanged. There are no backend or GraphQL changes.

Out of scope

  • The panel loads once. An assessment that finishes after the page has loaded only shows up on the next load. The merge widget behaves the same way.
  • No new feature spec. The full availability chain (licence, flag, flow catalog item, project enablement, stage check) is heavy to set up in a feature spec. The states are covered in Jest instead, and were checked by hand on a local GDK.
  • The merge widget's own risk extension is not touched.

Files changed

  • ee/app/assets/javascripts/merge_requests/ai_overview/components/risk_panel.vue (new)
  • ee/app/assets/javascripts/merge_requests/ai_overview/components/app.vue
  • ee/spec/frontend/merge_requests/ai_overview/components/risk_panel_spec.js (new)
  • ee/spec/frontend/merge_requests/ai_overview/components/app_spec.js - covers the panel rendering once the merge request loads, and not rendering when it cannot be read
  • ee/spec/frontend/merge_requests/ai_overview/mock_data.js - a risk assessment builder and a mock response

Verification

  • Jest, ee/spec/frontend/merge_requests/ai_overview: 112 examples, all passing (20 of them in the risk panel spec), re-run after rebasing onto master with the severity meter change (!253762 (merged)).
  • ESLint and Prettier: clean.
  • The existing feature spec, ee/spec/features/merge_request/user_toggles_ai_overview_spec.rb, re-run with the panel in place: 11 examples, 0 failures.
  • Manual check on a local GDK, no JavaScript console errors: enabled the risk classification flow for the project and seeded two assessments - one complete, high risk and medium confidence, with a rationale and three signals; one from before the latest push (stale), low risk and low confidence, with two signals that could not be measured. Checked the collapsed and expanded states on the first one. The screenshots above were retaken at head 7b68cda2, after the switch to the severity meter, so they show the description, labels and milestone panels too.
Edited by Marc Shaw

Merge request reports

Loading
Loading