Carry the classified revision in the shared resource context

What does this MR do and why?

The risk classification flow has to be told which merge request it is looking at, and at which revision. It asked for both in an envelope of its own, which repeated what GitLab already sends: every flow session receives the resource that started it in a shared envelope, agent_platform_resource_context.

The flow now reads the merge request from that shared envelope. The envelope carried no revision, so it gains one field for the merge request's current head SHA. Every field in it is required, so that is a new version of its schema, and the trigger path sends the new version.

The revision has to be sent rather than looked up when the flow submits. A score belongs to the diff the agent actually read, and the writeback endpoint refuses a submission naming any other revision. A merge request that gets a new push mid-run must not silently change what the score describes.

One envelope of the flow's own remains, carrying the risk domains to answer a claim about. That list comes from the domain catalog, so a domain added later reaches the agent with no change on the flow side.

Split out of !254545 (merged), which keeps the triggering mechanism. The two are independent and both target master. They touch adjacent lines of the flow definition, so whichever merges second needs a small rebase there.

References

Screenshots or screen recordings

Not applicable. Nothing user-facing changes: this is what GitLab sends to the agent platform.

How to set up and validate locally

The flow is ultimate_only, so the GDK needs an Ultimate licence, and one feature flag matters:

# Gates the flow itself.
Feature.enable(:duo_mr_risk_classification)

The "merge request created" event needs no flag of its own. It fires for any project that already has an active trigger.

All three parts have merged: this merge request, !254545 (merged), and gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!6872 (merged). Nothing needs checking out any more. A current master and a current ai-assist main are enough. After you pull the agent platform side, run gdk restart duo-workflow-service, because the flow registry fixes its config when the process starts.

  1. Turn the flow on for the top-level group. Go to the group, then Settings > GitLab Duo > Change configuration. Under Flow execution, select Allow flow execution and Allow foundational flows, select Risk Classification, then Save changes.

  2. Turn it on for the project. Go to the project, then Settings > General, expand GitLab Duo, turn on GitLab Duo, Allow flow execution and Allow foundational flows, then Save changes.

  3. Open a merge request, or mark an existing draft as ready. Then go to AI > Sessions in the project and open the Risk Classification session.

    The session gets past input validation and reaches its submit step. It used to stop at the first step with input 'risk_classification' does not match specified schema: 'merge_request_iid' is a required property - that error was what this MR removed, and it no longer happens on master.

  4. When the agent submits, the claims land on the assessment and the row moves on from pending:

    mr = MergeRequest.last
    
    mr.risk_assessment.status           # => "queued"
    mr.risk_assessment.classification   # => the claims the agent answered
    mr.risk_assessment.score            # => nil, scoring arrives separately
  5. Without running the flow at all, confirm what GitLab now sends. The revision travels in the shared envelope rather than one of the flow's own:

    mr = MergeRequest.opened.joins(:merge_request_diffs).first
    fields = Ai::DuoWorkflows::AdditionalContext::ResourceContextBuilder.build(mr)
    
    fields['merge_request_id']         # => the IID, as a string
    fields['merge_request_diff_sha']   # => the merge request head SHA
    
    schema = JSONSchemer.schema(Rails.root.join(
      'app/validators/json_schemas/agent_platform/agent_platform_resource_context/1.1.0.json'
    ))
    schema.valid?(fields)   # => true
  6. The flow's own envelope now carries the domains and nothing else:

    flow = Ai::Catalog::FoundationalFlow.risk_classification_v1
    envelope = flow.resolve_additional_context_for(resource: mr).sole
    
    envelope['Category']
    # => "agent_platform_risk_classification_context"
    
    payload = Gitlab::Json::SafeParser.parse(envelope['Content'])
    payload.keys                 # => ["domains"]
    payload['domains'].first     # => {"name" => "authorization", "description" => "Authorization, access control, or tenant isolation"}

    Add a domain under ee/lib/gitlab/duo/risk_classification/domains/ and it appears here, and in the agent's prompt, with no other change.

  7. Confirm a flow with no envelope of its own is unaffected, so this changes nothing for the other flows:

    Ai::Catalog::FoundationalFlow.convert_to_gl_ci_v1.resolve_additional_context_for(resource: mr)
    # => []

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Wanderson Policarpo

Merge request reports

Loading
Loading