Duo Messaging: workspace project
## Problem
The [Duo Messaging Service architecture (ADR 008)](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/19020) requires a CI pipeline to execute agent flows triggered from messaging services. CI pipelines fundamentally require a project (`Ci::Pipeline` validates `project: presence: true`), but messaging services like Slack have no project context.
## Proposal
Implement `Ai::Messaging::WorkspaceProjectService` — a find-or-create service that provides a `duo-workspace` project per top-level namespace:
- Finds an existing project by path or creates a new private project at the **root namespace** of the user's `duo_default_namespace` (e.g., `gitlab-org/duo-workspace`, not `gitlab-org/editor-extensions/duo-workspace`)
- One workspace project per top-level group — avoids proliferation across nested namespaces
- The project starts with a README explaining what it is and how to customize the agent environment, linking to relevant docs:
- [AGENTS.md customization files](https://docs.gitlab.com/user/duo_agent_platform/customize/agents_md/) for documenting project conventions
- [Configure flow execution](https://docs.gitlab.com/user/duo_agent_platform/flows/execution/) for `agent-config.yml` (Docker image, tooling, scripts)
- Follows the same auto-creation pattern as [Security Policy Projects](https://docs.gitlab.com/ee/user/application_security/policies/)
- Handles concurrent creation via `ActiveRecord::RecordNotUnique` rescue + retry lookup
### Service account: reuses `developer/v1` catalog SA
During implementation, we investigated three approaches for the messaging service account:
| Option | Approach | Tradeoff |
|--------|----------|----------|
| **A: Reuse `developer/v1` catalog SA** :white_check_mark: | Messaging uses the existing SA created when admin enables the Developer flow | Messaging triggers `developer/v1`, so it should use its SA |
| B: New `messaging/v1` foundational flow | New catalog entry with its own SA | No actual `messaging/v1` workflow definition exists — bends the abstraction |
| C: Self-bootstrapping SA (PoC approach) | Messaging creates its own SA on-demand | Redundant — creates a second SA for the same flow in the same namespace |
We chose **Option A**. Messaging is a trigger mechanism for `developer/v1`, not a separate flow. The SA identity reflects the flow being executed, not the trigger source. The originally scoped `ResolveServiceAccountService` was removed — the `TriggerFlowService` (issue #2) resolves the existing catalog SA instead.
### Scope
- `ee/app/services/ai/messaging/workspace_project_service.rb` + spec
No feature flag needed — this service is inert without a caller.
### MR
- !232332
## Related
- Parent issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/590434
- ADR: https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/19020
- Blocks: https://gitlab.com/gitlab-org/gitlab/-/work_items/597570
task