Add filters, roles and file links to the overview conversations

Adds filters, role pills and file links to the Conversations card added in !257832 (merged). Threads can be filtered to Needs action, People, Agents or All, each note gets a role pill (Agent, Author, Reviewer or Human), and diff threads link to their place on the Changes tab or show an Outdated badge.

!257832 (merged) has merged, so this now targets master. Next in the stack: !257871 (merged).

The card in the overview, opening on Needs action:

mr257879-overview

All: human and agent threads with their roles, a resolved thread dimmed, an outdated thread marked and current ones linked:

mr257879-all

Detailed context for AI agents

Why this changed

!257832 (merged) added the thread list itself. This MR adds the rest of the Decluttered Overview v2 design's Conversations card: the filters, the legend and border, role pills, and file links for diff threads. Reply, resolve and reopen are still in !257871 (merged). Code snippets are still in !257872 (merged).

Chain

Merged:

Open:

The conversations work was split into four MRs on request, to keep each one reviewable. Each is stacked on the one before it.

Rebase notes (2026-09-30)

  • !257832 (merged) was simplified during its review: it dropped the buildDiscussion mapping and read discussion.notes.nodes directly in the template. This MR brings buildDiscussion back, because each thread now needs derived fields (isAgent, replies, location, diffHref, isOutdated) that would otherwise be computed in the template several times.
  • !257832 (merged) also moved its strings from s__('AiOverview|...') to plain __(). The new strings here follow that, so none of them carry the AiOverview| namespace.
  • locale/gitlab.pot was regenerated after the rebase.

Review prep (2026-09-30)

Checked against iamphill's comments on the earlier MRs in the chain and adjusted three things before requesting review:

  • The file chip is a gl-badge with href and icon="doc-code" (as the releases page uses it) instead of a hand-built gl-link + gl-icon + span, and the outdated variant is the same badge without the link.
  • No raw palette classes. The legend swatches and the per-thread stripe use gl-bg-current with gl-text-status-info (human) or gl-text-status-brand (agent), and the unresolved dot uses gl-bg-current inside its danger badge. There is no solid semantic border token, so the stripe is an absolutely positioned 4px span rather than a gl-border-l-* class.
  • Dropped the comment on roleFor about future routing data, and the optional chain on position.diffRefs, which is non-null in the schema.

What this MR adds

  • Filters: Needs action (default), People, Agents, All, as a small segmented control on the right of the heading row. Hidden when there are no threads. The design opens on All; this starts on Needs action instead, since that is what still needs doing. When nothing needs action, the card falls back to All until another filter is picked.
  • A legend under the heading: a "Human" swatch in the status info colour and an "Agent" swatch in the status brand colour, hidden when there are no threads.
  • A 4px left stripe on each thread in the same two colours: info for a human-started thread, brand for an agent-started one. The design specs 3px, but that sits between steps of the spacing scale and lint blocks arbitrary values, so this rounds up to 4px.
  • A role pill on each note: "Agent" (tier variant) for bots, and for a person "Author" if they authored the merge request, else "Reviewer" if they are a reviewer, else "Human" (info variant). This uses the merge request's author and reviewers, which the main overview query already loads. conversation_note.vue gains an optional role prop.
  • A file chip (gl-badge) on diff threads. A current thread's chip links to its place on the Changes tab (withLinkedFileUrlParams, #note_<id>) instead of the note's own url, because the note's url opens the classic Discussions tab, which this design hides. An outdated thread's chip has no link and sits next to an "Outdated" badge, using the same test as DiffNote#active?: the position's diffRefs must match the merge request's. conversation_note.vue gains a default slot for the chip.
  • The discussions query asks for each note's position (filePath, oldPath, newPath, oldLine, newLine, diffRefs). The main overview query gains diffRefs, and discussions_panel.vue takes the merge request as a prop, like the other panels.

Review changes (2026-10-01)

Addresses iamphill's four comments in e1265469.

  • No separate context query. The mr_ai_overview_discussions_context.query.graphql query is deleted. The main overview query (mr_ai_overview.query.graphql) already loaded webPath, author and reviewers, and now also asks for diffRefs { baseSha startSha headSha }. app.vue passes :merge-request="mergeRequest" to <discussions-panel>, which gains a required mergeRequest prop. Side benefit: the app's mergeRequestReviewersUpdated subscription now keeps the Reviewer pills current. The panel's own Apollo result is renamed from mergeRequest to connection to avoid clashing with the prop.
  • Default filter. filter starts as null, and a computed activeFilter (getter and setter) falls back to all when the open count is 0, otherwise action. Once the user picks a filter it sticks. The empty message is unchanged.
  • Roles in buildDiscussion. It sets role: this.roleFor(note.author) on the discussion and maps replies to { note, role }. The template reads discussion.role and reply.role, so it no longer calls roleFor per note on every render.
  • The segmented control uses v-model="activeFilter" instead of :value plus @input.

Strings

Six new strings: Agent, Agents, Filter conversations, Needs action, Outdated, People.

Reused: All, Author, Reviewer, Human.

Files changed (11)

  • ee/app/assets/javascripts/merge_requests/ai_overview/components/app.vue - passes the merge request to the discussions panel.
  • ee/app/assets/javascripts/merge_requests/ai_overview/components/discussions_panel.vue
  • ee/app/assets/javascripts/merge_requests/ai_overview/components/conversation_note.vue
  • ee/app/assets/javascripts/merge_requests/ai_overview/queries/mr_ai_overview.query.graphql - adds diffRefs.
  • ee/app/assets/javascripts/merge_requests/ai_overview/queries/mr_ai_overview_discussions.query.graphql
  • ee/spec/features/merge_request/user_toggles_ai_overview_spec.rb - adds: the diff thread links to the diffs path with file_path.
  • ee/spec/frontend/merge_requests/ai_overview/components/app_spec.js - asserts the merge request is handed to the discussions panel.
  • ee/spec/frontend/merge_requests/ai_overview/components/discussions_panel_spec.js - 28 examples now: adds the default filter and order, opening on All when nothing needs action, the legend, roles, the stripe, the diff link, no location, each filter, an outdated thread, and no match.
  • ee/spec/frontend/merge_requests/ai_overview/components/conversation_note_spec.js - 6 examples: adds the role and its absence.
  • ee/spec/frontend/merge_requests/ai_overview/mock_data.js - diffRefs, positions, and the merge request's author, reviewers, diffRefs and webPath.
  • locale/gitlab.pot.

Verification performed (after the rebase, 2026-09-30)

  • Jest ee/spec/frontend/merge_requests/ai_overview: 9 suites, 146 examples, all pass.
  • ESLint (including the GraphQL query against a fresh schema dump), Prettier and Rubocop clean.
  • Feature spec ee/spec/features/merge_request/user_toggles_ai_overview_spec.rb ran locally on GDK2: 14 examples. Two examples unrelated to this card (the merge-check row and the reviewers list) timed out on the first cold Vite run and passed on a rerun.
  • The screenshots above were taken on GDK2 with this branch, on a seeded merge request with an author thread, reviewer threads (one resolved), agent threads, two current diff threads and one outdated diff thread. No JavaScript console errors.
  • Following a current diff thread's link opened the Changes tab (Rapid Diffs) with the file selected and the thread in view (checked before the rebase).

Verification performed (after the review changes, 2026-10-01)

  • Jest ee/spec/frontend/merge_requests/ai_overview: 9 suites, 147 examples, all pass. One new example, "opens on All instead of an empty list". app_spec.js now asserts the merge request is handed to the discussions panel.
  • ESLint (including the GraphQL rule against a fresh schema dump) and Prettier clean.
  • The conversations example in ee/spec/features/merge_request/user_toggles_ai_overview_spec.rb passed on GDK2.
Edited by Marc Shaw

Merge request reports

Loading
Loading