# Teams Reference GitHub Teams or GitLab groups directly in your config with the `@` prefix — the same way you would mention them on your platform. Team references can be used anywhere a list of *users* is expected: `reviewers`, `alternates`, `authors`, `cc`, and inside [alias](config/aliases.md) values. ```toml [[scopes]] name = "app" paths = ["app/**/*"] reviewers = ["@acme/ios-devs"] ``` Team membership is managed on your platform and synced by PullApprove, so the team stays the single source of truth — there is no user list to maintain in the config. ## Syntax - `@org/team-slug` — the org (or GitLab top-level group) prefix is required and must match the organization the repo belongs to. - References work alongside plain usernames and `$` aliases: `reviewers = ["alice", "$backend", "@acme/ios-devs"]`. - In `reviewers`, `alternates`, and `cc`, negation subtracts from the resolved list: `"!@acme/contractors"` removes that team's members from the list built by the rest of the entries. In `authors`, negation is a match-time rule instead (see [`authors`](config/scopes/authors.md)) — team refs there still expand to individual `!member` entries, which is unaffected. Combining `"*"` with a negated entry in `reviewers`/`alternates`/`cc` is a configuration error. ```toml [[scopes]] name = "ios" paths = ["ios/**/*"] reviewers = ["@acme/ios", "!contractor"] ``` - Team references are only valid in user lists. They cannot be used in `paths`, `labels`, or `code`. ```toml [aliases] release = ["@acme/release-eng", "@acme/ios-leads", "sarah"] [[scopes]] name = "release" paths = ["config/release/**/*"] reviewers = ["$release"] require = 2 ``` ## How it works PullApprove syncs team membership from your platform — instantly from membership webhooks where available, and otherwise refreshing as your pull requests are processed once the last sync is an hour old. When a pull request is processed, team references expand to the current member usernames, exactly like aliases. The expanded usernames are stored with each processing result, so every PR records exactly who the team was at the time it was evaluated — the membership history is auditable per PR, not just "whatever the team is today." A team reference that doesn't match a known team is a **configuration error** — the same loud failure as an unknown `$alias`, so a typo can't silently drop reviewers. And because PullApprove validates config changes in the PR that proposes them, a bad team reference fails *before* it merges. ## GitHub - Reference teams by their slug: `@org/team-slug`. Slugs are lowercase and flat — a nested team's slug does not include its parent (`@acme/ios`, not `@acme/mobile/ios`). - **Nested teams roll up.** A parent team's membership includes the members of all its child teams, matching GitHub's own behavior. - Teams synced from your identity provider (Entra ID, Okta, LDAP) work like any other team — PullApprove sees the resolved membership. ## GitLab - Reference groups by their full path: `@acme/mobile/ios`. Any subgroup of your connected top-level group can be referenced. - **Direct members only.** Unlike GitHub, members inherited from ancestor groups are *not* included — a subgroup reference means the people directly in that subgroup, not everyone above it. A subgroup whose members are all inherited resolves as an empty team — add direct members to a subgroup you want to reference. - Bot users (such as group access token bots) are excluded automatically. - Renaming or moving a group changes its path. Update config references to the new path — references to the old path stop resolving at the next team sync (they may keep working briefly, with membership staying current in the meantime). ## FAQs ### Should I use a team or an alias? Use a **team** when the group already exists on your platform and is maintained there — access control and review ownership stay in sync automatically. Use an **alias** when the grouping is review-specific and doesn't correspond to a real team, or when you want the membership list visible in the config itself. ### What happens if a team is empty? An empty team expands to no reviewers, same as an alias with an empty list. If a scope's reviewers resolve to nobody and `require` can no longer be met, that's a **configuration error** — the same loud failure as an unknown team or unknown alias, not a silent pending state. ### Does the CLI expand teams? The CLI validates team reference syntax but doesn't expand membership — that requires platform API access. Server-side processing always expands them.