Draft: Add compliance control: duo_agentic_flow_governance_oversight_configured
What does this MR do and why?
Adds the duo_agentic_flow_governance_oversight_configured compliance control (control #18 (closed)) to the compliance requirements controls framework.
The EU AI Act (Art. 14, 17) and NIST GV-4.3/MS-2.5 require that human oversight measures exist for high-risk AI systems. This control verifies that Duo Agent Platform flows have those measures in place.
Implementation
The control is implemented as a project field in the compliance requirements framework:
- New field method
duo_agentic_flow_governance_oversight_configured?inComplianceManagement::ComplianceRequirements::ProjectFields - Control registered in
ee/config/compliance_management/compliance_controls.yml(boolean,enum_value: 67) andee/config/compliance_management/requirement_controls.json - Field added to the control expression JSON schema
- Frontend
statuses_info.jsentry (title, description, remediation guidance) for the standards adherence details drawer - Documentation entry in the compliance frameworks controls table
What the field checks (configuration-based, not event history)
- Flow trigger configured: At least one active
Ai::FlowTriggerapplies to the project (direct model check, replacing theflow_trigger_created90-day audit event look-back). UsesAi::FlowTrigger.applicable_to_project, the same scope as the firing path: active project triggers plus inherited root-group triggers (inherited only whileai_catalog_group_inherited_consumersis enabled). Inactive triggers count neither for presence nor for broad-scope detection. - MCP explicitly configured: The root ancestor's AI settings have the
duo_workflow_mcp_enabledkey inconfigured_at(per-setting provenance).Groups::UpdateServicewrites this key only when the setting is explicitly submitted, even if the submitted value equals the default. Record presence alone is not a signal. See !256692 (merged). - No ungated broad-scope trigger: A trigger is broad-scope when it is autonomous (
scheduled, perAi::FlowTrigger#autonomous_only?: no human initiator, not branch-scoped), or when it is apipeline_hookstrigger whose filter does not restrict the ref. Ref scoping followsGitlab::FilterEvaluatorsemantics: anallgroup restricts if any child restricts, ananygroup only if every child restricts, and a leaf restricts only forobject_attributes.refwitheq/in, orobject_attributes.default_brancheq true.ne,not_in,contains, andnot_containsdo not count. If any broad-scope trigger exists, the project must have at least one linked, enforced (non-warn-mode) approval policy with a rule covering all protected branches (branches: []orbranch_type: protected). Approval policies gate merge requests by target branch while the trigger fires on any push, so there is no per-trigger match; requiring approval on every protected branch is the strongest gate expressible with current policy data. Linked policies are alreadyenabledand scope-applicable to the project (Security::PolicyProjectLinkis only created under those conditions).
Short-circuits:
- Returns
falsewhen the project has no group (personal namespace projects have no group-level MCP setting). - Returns
true(not applicable) when the Duo Agent Platform is unavailable for the group. TODO references https://gitlab.com/gitlab-org/gitlab/-/work_items/621132 for proper NOT_APPLICABLE semantics — same pattern as !252332 (merged).
Why configuration-based (not event history)
The original spec required querying historical audit events. This was dropped because:
- Checking historical event occurrences measures product usage, not compliance configuration
- A correctly configured but low-traffic project would fail purely from inactivity
- Neither EU AI Act Art. 14/17 nor NIST GV-4.3/MS-2.5 impose time-based activity requirements
This follows the same pattern as the sibling control duo_agentic_flow_audit_events_streaming (!252332 (merged)).
Database
No schema changes. New read queries introduced by duo_agentic_flow_governance_oversight_configured? (all executed once per project evaluation; flow triggers are loaded once and reused):
1. Active flow triggers applicable to the project
Ai::FlowTrigger.applicable_to_project(project), the same query Ai::FlowTriggers::EventTriggerService runs on the firing path (without the triggered_on condition). Still one ai_flow_triggers query. When ai_catalog_group_inherited_consumers is enabled for the root group:
SELECT "ai_flow_triggers".* FROM (
(SELECT "ai_flow_triggers".* FROM "ai_flow_triggers"
WHERE "ai_flow_triggers"."project_id" = 86434914 AND "ai_flow_triggers"."active" = TRUE)
UNION ALL
(SELECT "ai_flow_triggers".* FROM "ai_flow_triggers"
INNER JOIN "ai_catalog_item_consumers" ON "ai_catalog_item_consumers"."id" = "ai_flow_triggers"."ai_catalog_item_consumer_id"
WHERE "ai_catalog_item_consumers"."group_id" = 9970 AND "ai_catalog_item_consumers"."kind" = 1
AND "ai_flow_triggers"."active" = TRUE)
) ai_flow_triggersWhen the flag is disabled, only the first branch runs:
SELECT "ai_flow_triggers".* FROM "ai_flow_triggers"
WHERE "ai_flow_triggers"."project_id" = 86434914 AND "ai_flow_triggers"."active" = TRUEThe SQL above was derived from the scope definitions and not captured from a running instance (the development environment had no database). New plans for the union are pending. Previous plan for the project-only query (before the active condition): https://console.postgres.ai/gitlab/gitlab-production-main/sessions/57132/commands/496770 (Index Scan on index_ai_flow_triggers_on_project_id, 3 rows, 2.2 ms). Also for gitlab-org/gitlab (278964, 26 rows, 60 ms cold): https://console.postgres.ai/gitlab/gitlab-production-main/sessions/57132/commands/496755
2. Root ancestor AI settings
SELECT "namespace_ai_settings".* FROM "namespace_ai_settings" WHERE "namespace_ai_settings"."namespace_id" = 9970 LIMIT 1Plan: https://console.postgres.ai/gitlab/gitlab-production-main/sessions/57132/commands/496779 (Index Scan on index_namespace_ai_settings_on_namespace_id, 4.6 ms)
3. Enforced approval policies linked to the project
Only runs when a broad-scope trigger (autonomous, or pipeline_hooks without ref scoping) exists.
SELECT "security_policies".* FROM "security_policies"
INNER JOIN "security_policy_project_links" ON "security_policies"."id" = "security_policy_project_links"."security_policy_id"
WHERE "security_policy_project_links"."project_id" = 86434914
AND "security_policies"."type" = 0
AND (content->>'enforcement_type' IS DISTINCT FROM 'warn')Plan: https://console.postgres.ai/gitlab/gitlab-production-main/sessions/57132/commands/496772 (Nested Loop over idx_security_policy_project_links_on_project_id_and_id + security_policies_pkey, 3 rows, 3.7 ms)
4. Undeleted rules for those policies (preload)
SELECT "approval_policy_rules".* FROM "approval_policy_rules"
WHERE (rule_index >= 0) AND "approval_policy_rules"."security_policy_id" IN (2480423, 2480422, 2480424)Plan: https://console.postgres.ai/gitlab/gitlab-production-main/sessions/57132/commands/496777 (Index Scan on idx_approval_policy_rules_security_policy_id_id, 3 rows, 10 ms)
References
- Issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/618334
- Sibling control MR: !252332 (merged)
- Compliance Framework Internal Controls Specification: https://gitlab.com/gitlab-org/gitlab/-/issues/604730
- NOT_APPLICABLE semantics tracking: https://gitlab.com/gitlab-org/gitlab/-/work_items/621132
Screenshots or screen recordings
N/A - no user-facing UI changes beyond the new control's entry in the standards adherence details drawer.
How to set up and validate locally
- Create a group with a project
- Enable the Duo Agent Platform for the group
- Create an
Ai::FlowTriggerfor the project, then record MCP provenance on the root group:Groups::UpdateService.new(group, owner, ai_settings_attributes: { duo_workflow_mcp_enabled: true }).execute - Evaluate the field in a Rails console:
project = Project.find(<project_id>)
ComplianceManagement::ComplianceRequirements::ProjectFields.map_field(
project, 'duo_agentic_flow_governance_oversight_configured'
)
# => true (compliant) / false (non-compliant)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.