Accept trigger goals when enabling a catalog item
What does this MR do and why?
This MR adds a triggerGoals argument to two mutations:
aiCatalogItemConsumerCreate and aiCatalogItemConsumerBulkCreate.
A goal tells a Duo Agent Platform flow what to do. A flow trigger stores
its goals in the goals column, keyed by event type name. Both
mutations create a flow trigger when a user enables a catalog item.
They could set triggerTypes and triggerFilter, but not a goal. The
two trigger creation paths did not match. This MR closes that gap.
triggerGoals follows the route that triggerFilter already takes. The
bulk path carries it through EnqueueBulkCreateService,
BulkCreateWorker and BulkCreateService. CreateService puts it on
the new trigger as goals.
The argument type is GraphQL::Types::JSON. This matches the goals
argument that !258739 (merged) added. The goals payload keeps one shape across
all three mutations. The ai_flow_trigger_goals JSON schema stays the
single contract. A typed input object would repeat the event type keys
and could drift from that schema.
Decisions that need a close look:
-
CreateService#flow_triggers_not_allowed?now also checkstrigger_goals. On a group level enable, the key was never deleted. It reachedItemConsumer.newand raisedActiveModel::UnknownAttributeError. The user now gets the existing "Triggers can't be added" message. -
foundational_flow_with_user_triggers?does not check goals on purpose. A foundational flow can opt in later withaccepts_trigger_goal: true. A check here would refuse an opted-in flow. The model validationgoals_supported_by_targetalready refuses a goal on a flow that does not accept one. -
The new guard raised the cyclomatic complexity of
EnqueueBulkCreateService#executeabove the limit. I did not disable the cop. I moved the three "not supported for this item type" checks intounsupported_trigger_argument_message. This touches two existing lines. -
prepare_trigger_paramsnow deletestrigger_types,trigger_filterandtrigger_goalsbefore its early returns. The guard in point 1 tests the params for truth, so it cannot see an explicitnull. GraphQL sends one as a key that holdsnil. The key then survived and reachedItemConsumer.new, which has no such attribute, so the user got a 500. A null goal now means "no goal", which is what the trigger form will send for an empty field.trigger_typeswas deleted inside the custom flow branch, which three early returns can skip: a group level enable, a foundational flow, and a custom flow that sends no types. That fault pre-dates this MR. @Imjaydip found it in review, and it is fixed here because the same method had to change anyway. There is a spec for it beside the null goal spec.
Sidekiq compatibility: worker_params calls compact. A job that is
already queued has no trigger_goals key. The worker reads nil. No
worker versioning is needed.
Feature flag: the ai_flow_trigger_goals flag exists and is off by
default. This MR adds no new flag check. The model validation
goals_available refuses a goal while the flag is off. The user gets a
clear error. This follows !258739 (merged).
This is MR 3.1 in a stack:
- !256658 (merged) added the
goalscolumn and its validations. - !258739 (merged) added the
goalsGraphQL field and mutation arguments. - !257218 (merged) (open) composes the goal at dispatch time.
- MR 4 (not started) adds the trigger form field and user documentation.
References
- Work item: #627896
- Note that asked for this MR: !258739 (comment 3940605804)
- MR 1: !256658 (merged)
- MR 2: !257218 (merged)
- MR 3: !258739 (merged)
Screenshots or screen recordings
This MR has no UI change.
How to set up and validate locally
-
In the Rails console, turn the flag on:
Feature.enable(:ai_flow_trigger_goals) -
Find a custom AI Catalog flow and a project. Open GraphiQL at
/-/graphql-explorer. Enable the flow for the project and send a goal. The event type key must keep its underscores, so send the variables as JSON.mutation($input: AiCatalogItemConsumerCreateInput!) { aiCatalogItemConsumerCreate(input: $input) { errors } }{ "input": { "target": { "projectId": "gid://gitlab/Project/<id>" }, "itemId": "gid://gitlab/Ai::Catalog::Item/<id>", "triggerTypes": ["pipeline_hooks"], "triggerGoals": { "pipeline_hooks": "Summarise the failing jobs." } } } -
In the Rails console, confirm the goal is stored:
Ai::FlowTrigger.last.goals -
Repeat with
aiCatalogItemConsumerBulkCreateand two project IDs. Confirm that each project's trigger holds the goal. -
Turn the flag off with
Feature.disable(:ai_flow_trigger_goals). Send the same mutation. Confirm that the response holds the error "Flow trigger goals are not available". Confirm that no trigger is created. -
Send a goal for an event type that is not in
triggerTypes. Confirm that the response names the unused event type. -
Enable an agent or flow for a group and send
"triggerGoals": null. Confirm that the enable succeeds and that no trigger is created. Before this MR the response was a 500.
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.