Move developer flow goal construction from AIGW to Rails
What does this MR do?
Moves the goal routing logic for the developer/v1 flow from the AIGW Jinja prompt template into Rails. Each trigger point now constructs a complete natural-language goal, so the AIGW prompt can simply pass it through as {{ goal }}.
The frontend changes ("Generate MR with Duo" button) have been split into a separate MR: !232720 (merged)
Problem
Currently in the developer flow in AIGW, the prompt has fragile string-matching ('Input:' in goal, '/-/merge_requests/' in goal) to route different trigger types to different instructions. Adding new trigger types required changes to the AIGW prompt template. The logic on how an agent should generally behave should be kept in the system prompt to make it a reusable building block. The user prompt (where we pass the goal) should be purposed for the task.
We want to get to a point where the developer can be used in a flexible way and where adding new triggers (e.g. like here) is straightforward.
Note that the current developer/v1 will support custom goals better after this is merged: gitlab-org/modelops/applied-ml/code-suggestions/ai-assist!5265 (merged) . Before that, this MR would be blocked. We are also working on a new developer_unstable that should ship with %19.0 which then would just pass through the user goal when this MR makes it in (follow-up work to be done).
Solution
Goal templates are defined via a GoalTemplates::Base contract and stored in flow-specific subclasses (e.g. GoalTemplates::Developer). The base class provides a shared safe interpolation helper; subclasses implement resolve to map event types to templates. Templates are keyed by event type and resource type, resolved at the trigger point in Rails.
- Mention triggers: Goal with the note URL (
#note_123) and the mention content in XML tags - Assign triggers (work items/issues): Goal to solve the assigned work item and create an MR
- Assign triggers (merge requests): Goal to assess MR state (pipelines, review feedback, discussions) and act accordingly
- Assign reviewer triggers: Goal to review MR diffs and leave comments with suggestion blocks
- IDE/LSP: Already sends free-form goals, no change needed
Why goals are constructed server-side
This MR moves goal construction from the AIGW Jinja prompt template to Rails. This separates two concerns:
- System prompt (AIGW): Defines how the agent behaves — its personality, reasoning approach, tool usage patterns. This lives in the AI Gateway and iterates independently of GitLab releases.
- User prompt / goal (Rails): Defines what task the agent should perform. Constructed by Rails based on the trigger context (mentions, assigns, button clicks), versioned with the GitLab release.
Why not keep goals in AIGW? Previously, the AIGW prompt contained routing logic to decide what instructions to give based on the trigger type. Every new trigger required a coordinated AIGW change. By constructing complete goals in Rails, AIGW just passes {{ goal }} through.
Why not store goals in a prompt registry? Goal templates are tightly coupled to the trigger context (resource type, event, note URL). They are versioned with the GitLab release, which provides auditability. A prompt registry adds indirection without clear benefit here.
Key changes
| File | Change |
|---|---|
ee/app/models/ai/catalog/goal_templates/base.rb |
Base class with resolve contract and safe interpolate helper |
ee/app/models/ai/catalog/goal_templates/developer.rb |
Goal templates for mention, assign, assign_reviewer with resource-type routing |
ee/app/models/ai/catalog/foundational_flow.rb |
Added goal_templates attribute |
ee/app/services/ai/flow_triggers/run_service.rb |
Template-based goal resolution via goal_templates |
ee/app/services/ee/notes/post_process_service.rb |
Passes note_id for mention triggers |
Deployment note
The companion AIGW change (simplifying the developer/v1 user prompt to {{ goal }}) should be deployed first or simultaneously. Without it, the enriched goals may be double-wrapped by the existing Jinja if/else routing.
Related: #574990
How to set up and validate locally
Prerequisites
- A Duo Developer trigger configured on a test project with the
developer/v1flow (with both assign and mention event types)
Test 1: Mention trigger
- Go to an issue in the test project
- Write a comment mentioning the Duo Developer service account:
@duo-developer Please summarize the discussion - Verify in Langsmith trace that the user prompt matches the
MENTION_TEMPLATE:- Contains
You were mentioned in a note on this issue:followed by the issue URL with#note_<id> - Mention text wrapped in
<mention>XML tags
- Contains
Test 2: Assign trigger on issue
- Assign the Duo Developer service account to an issue
- Verify in Langsmith trace that the user prompt matches
ASSIGN_ISSUE_TEMPLATE:- Contains
You were assigned to solve the following issue:followed by the issue URL
- Contains
Test 3: Assign trigger on MR
- Assign the Duo Developer service account to a merge request (not as reviewer)
- Verify in Langsmith trace that the user prompt matches
ASSIGN_MERGE_REQUEST_TEMPLATE:- Contains
You were assigned to a merge request:followed by the MR URL - Includes instructions about fetching diffs, pipeline status, and review comments
- Contains
Test 4: Assign as reviewer on MR
- Assign the Duo Developer service account as a reviewer on a merge request
- Verify in Langsmith trace that the user prompt matches
ASSIGN_MERGE_REQUEST_REVIEW_TEMPLATE:- Contains
You were assigned as a reviewer on a merge request: - Includes instructions about suggestion blocks and not approving
- Contains
Test 5: Fix pipeline flow (regression check)
- Set up a trigger for the
fix_pipeline/v1flow on a test project - Push a commit that causes a pipeline failure
- Verify: The fix pipeline flow triggers and receives the pipeline URL as its goal
Test 6: User input with %{...} sequences (safety check)
- Write a comment:
@duo-developer Fix the %{resource_url} formatting in the README - Verify in Langsmith trace: The literal
%{resource_url}appears inside<mention>— not substituted