Spike: Project Context Init Flow
Create a new flow for new projects to add context for use by Duo Agent Platform.
* Creates a basic AGENTS.md and potential sub AGENTS.md (same as init command of CLIs) in a new MR
* Subfolders created for skills, rules, etc. and adds README files
| Screenshots |
| ------ |
| {width=900 height=125}|
|{width=900 height=132}|
|{width=900 height=282}|
|{width=900 height=547}|
## Implementation Plan
### AI Gateway (`ai-assist` repo)
Related MR: https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/merge_requests/4851
1. Add `duo_workflow_service/agent_platform/v1/flows/configs/project_context_init/1.0.0.yml` to the flow registry with two components:
- `context_analyzer` (`AgentComponent`) — reads the repo tree, detects languages, existing agent context files (AGENTS.md, CLAUDE.md, CURSOR.md, .cursorrules, etc.), and tooling configs. Retrieves the current user ID via `get_current_user` for MR assignment. Toolset: `list_dir`, `find_files`, `read_file`, `get_project`, `get_current_user`
- `init_writer` (`AgentComponent`) — generates a minimal, evidence-grounded `AGENTS.md` (no boilerplate; no `skills/`/`rules/` stubs), optional sub-`AGENTS.md` only for subdirs with distinct tooling, then opens a draft MR assigned to the initiating user. Toolset: `create_file_with_contents`, `mkdir`, `create_branch`, `run_git_command`, `create_merge_request`
2. Add prompts for both components under `ai_gateway/prompts/definitions/`
3. Add model selection entries for both components in `ai_gateway/model_selection/unit_primitives.yml` (`unit_primitive: duo_agent_platform`, default model: `claude_sonnet_4_6`)
### GitLab Monolith (`gitlab` repo)
All monolith changes are gated behind the `duo_project_context_init` feature flag (type: `beta`, group: `group::ai coding`, default: disabled). The flag must be checked at every entry point: the controller, the service, the sidebar, and the frontend route.
#### Feature Flag Definition
Create `ee/config/feature_flags/beta/duo_project_context_init.yml`:
```yaml
name: duo_project_context_init
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/226355
rollout_issue_url: # add rollout issue URL
milestone: '17.11'
group: group::ai coding
type: beta
default_enabled: false
```
Run `bin/feature-flag duo_project_context_init --type beta` to generate this file.
#### 1. Backend: `ExecuteWorkflowService` validation enhancement
**File**: `ee/app/services/ai/catalog/execute_workflow_service.rb`
Modify `validate` to accept `flow_name_reference?` as a bypass for the `json_config` requirement:
```ruby
def validate
return error('You have insufficient permissions') unless allowed?
return error('JSON config is required') unless json_config.present? || foundational_flow? || flow_name_reference?
return error('Goal is required') unless goal.present?
ServiceResponse.success
end
def flow_name_reference?
@flow_definition.present? && !foundational_flow?
end
```
When `json_config` is nil, `StartWorkflowService#serialized_duo_flow_config` already returns nil, so `DUO_WORKFLOW_FLOW_CONFIG` is not set. The AI Gateway executor then resolves the flow YAML from the registry by name automatically via `FlowConfig.from_yaml_config`.
#### 2. Backend: `InitProjectContextService`
**File**: `ee/app/services/ai/duo_workflows/init_project_context_service.rb`
New service that calls `ExecuteWorkflowService` with:
- `flow_definition: "project_context_init/v1"`
- `container: project`
- `goal: "Initialize project context for Duo Agent Platform"`
- No `json_config` needed
Guard the service with the feature flag:
```ruby
def execute
return ServiceResponse.error(message: 'Feature not available') unless Feature.enabled?(:duo_project_context_init, project)
# ... call ExecuteWorkflowService
end
```
#### 3. Backend: Controller
**File**: `ee/app/controllers/projects/duo_agents_platform_controller.rb`
- Add `'onboarding'` to `DUO_AGENT_PLATFORM_ROUTES` for correct `feature_category`
- Add `'onboarding'` to `specific_vueroute?` and `authorized_for_route?` (accessible to any user who can `:duo_workflow` on the project)
- Add `set_has_agents_md` before_action scoped to the onboarding route only, gated by the feature flag:
```ruby
before_action :set_has_agents_md, only: [:show]
def set_has_agents_md
return unless params[:vueroute] == 'onboarding'
return unless Feature.enabled?(:duo_project_context_init, @project)
gon.push(has_agents_md: agents_md_exists?)
end
```
Where `agents_md_exists?` checks for `AGENTS.md` in both the project root and `.ai/AGENTS.md`.
#### 4. Backend: Rails routes
**File**: `ee/config/routes/project.rb`
Add a named route helper for the sidebar:
```ruby
scope :automate do
get '/(*vueroute)' => 'duo_agents_platform#show', as: :automate, format: false
get 'onboarding', to: 'duo_agents_platform#show', as: :automate_onboarding, format: false
end
```
#### 5. Backend: Sidebar
**File**: `ee/lib/sidebars/projects/super_sidebar_menus/duo_agents_menu.rb`
Add an "Onboarding" menu item, rendered only when the feature flag is enabled:
```ruby
override :configure_menu_items
def configure_menu_items
add_item(duo_agents_runs_menu_item)
add_item(duo_agents_onboarding_menu_item) if Feature.enabled?(:duo_project_context_init, context.project)
true
end
def duo_agents_onboarding_menu_item
::Sidebars::MenuItem.new(
title: s_('DuoAgentsPlatform|Onboarding'),
link: project_automate_onboarding_path(context.project),
active_routes: { controller: :duo_agents_platform },
item_id: :agents_onboarding
)
end
```
#### 6. Frontend: Router constants
**File**: `ee/app/assets/javascripts/ai/duo_agents_platform/router/constants.js`
```javascript
export const AGENTS_PLATFORM_ONBOARDING_ROUTE = 'onboarding_index';
```
#### 7. Frontend: Router
**File**: `ee/app/assets/javascripts/ai/duo_agents_platform/router/index.js`
Add a new top-level route tree, conditionally included when `glFeatures.duoProjectContextInit` is enabled:
```javascript
...(glFeatures.duoProjectContextInit ? [{
component: NestedRouteApp,
path: '/onboarding',
meta: { text: s__('DuoAgentsPlatform|Onboarding') },
children: [
{
name: AGENTS_PLATFORM_ONBOARDING_ROUTE,
path: '',
component: ProjectContextOnboardingPage,
},
],
}] : []),
```
This gives the URL `/automate/onboarding` and the breadcrumb "Automate > Onboarding" automatically via the existing router-driven breadcrumb system.
#### 8. Frontend: Onboarding page component
**File**: `ee/app/assets/javascripts/ai/duo_agents_platform/pages/onboarding/project_context_onboarding_page.vue`
A full page component with:
- `PageHeading` with title "Set up your project for Duo Agent Platform"
- Description explaining what the init flow does (creates `AGENTS.md`, sub-files, skill/rule stubs)
- **AGENTS.md absent state**: primary "Initialize project context" `GlButton` that calls `InitProjectContextService`; button is disabled with a spinner while the flow session is in progress
- **AGENTS.md present state**: a `GlAlert` (variant: success) informing the user that context has already been initialized, with a link to the resulting MR
- Reads `hasAgentsMd` from `window.gon?.has_agents_md` (injected by the controller) to determine which state to render
- On success, shows the success state in-page (no full redirect needed)
### What is explicitly NOT needed
- No new API endpoint
- No AI Catalog registration or `ItemConsumer`
- No foundational flow registration (avoids appearing in the group settings UI)
- No full YAML bundled in the monolith
- No new DB column to track init state (file presence is the guard)
- No `skills/` or `rules/` directory stubs (removed from scope)
issue