Split auto-generated principles sync MR per SSOT-owning team
## Problem The weekly principles-sync auto-MR currently routes all affected distilled files through **one MR** (e.g. https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235635 with 23 files changed). The only CODEOWNERS rule that fires on `/.ai/` points at 4 AI-tooling DRIs — so the merge can proceed with **1 approval from any of 4 people**, regardless of which domains the distilled content touched. This is too coarse for cross-domain content. Each distilled file in `.ai/principles/distilled/<name>.md` is a domain-specific checklist derived from an SSOT doc owned by a specific team: - `security.md` → `@gitlab-com/gl-security/appsec` - `database-migrations.md` → `@gitlab-org/maintainers/database` - `graphql.md` → `@gitlab-org/maintainers/rails-backend` - `frontend-vue.md` → `@gitlab-org/maintainers/frontend` - `qa-rspec.md` → `@abdwdd @alexpooley` - etc. (~10–11 distinct teams across the current 23 principles) AppSec review on the gem (see https://gitlab.com/gitlab-com/team-member-epics/access-requests/-/issues/43931) flagged this as the right thing to fix. ## Proposal Refactor `Sync#publish` and `Sync::AutoMr#create_branch_and_mr` to **fan out by SSOT-team owner**: - Add an `owner_team:` field per principle in `.ai/principles/manifest.yml`. Primary owner only; secondary teams go into the MR description as "request review from" mentions. - Group affected principles by `owner_team`. For each unique team: - Create branch `docs-sync/principles-<date>-<team-slug>` from `origin/master`. - Write only that team's distilled files. - Push and open one MR per team. CODEOWNERS routes the approval correctly because each MR only touches files the SSOT team owns. - AGENTS.md, CLAUDE.md, and `.{agents,claude}/skills/gitlab-coding-principles/SKILL.md` are global (they embed the full routing table for all principles). Carve those out into a separate **tooling MR** routed to AI-tooling DRIs, since SSOT teams have no stake in their content. - Same-day re-run idempotency: `find_open_mr_iid` becomes per-team; the script looks up each team's open MR and updates in place. ## Implementation outline 1. **Manifest schema**: add `owner_team:` per principle. Add a `team_slug:` for the branch name suffix (derived from team handle, e.g. `database` from `@gitlab-org/maintainers/database`). 2. **Sync::AutoMr#create_branch_and_mr**: replace the single-branch + single-MR flow with a per-team fan-out. Push N branches (one per affected team) plus 1 tooling branch. 3. **Sync::AutoMr#create_mr**: parameterise the title template by team. Add per-team description content (only that team's principles in the per-principle sections). 4. **Per-team idempotency**: `find_open_mr_iid` runs once per team branch. Same-day re-runs update each team's existing MR in place rather than creating duplicates. 5. **Specs**: new fixtures covering multi-team affected sets, tooling-MR separation, per-team idempotency on re-run, edge cases (only one team affected, no team-owner specified, etc.). ## Scope This issue tracks **MR-C**, stacked on top of MR-A (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235272) and **independent of MR-B** (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235014). The CI YAML doesn't change. ## Out of scope - Changing CODEOWNERS for the gem itself. - Reworking how distillation triggers (still weekly schedule, still through the Duo Workflow API). - Cross-team principles where the SSOT spans multiple owners: primary owner gets the MR; secondary teams are mentioned in description. ## Related - AR sign-off conditions: https://gitlab.com/gitlab-com/team-member-epics/access-requests/-/issues/43931 - MR-A (gem + flow): https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235272 - MR-B (CI job): https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235014 - Example under-routed auto-MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/235635 - Parent epic: gitlab-org&amp;21742
issue