From a667c4306adc2f0abe36e3648a97fc51ecc03ef2 Mon Sep 17 00:00:00 2001 From: David Soria Parra Date: Thu, 15 Jan 2026 14:16:06 +0000 Subject: [PATCH] Add Discord role inheritance via parent traversal and discordImplies - Add discordImplies field to Role interface for Discord-specific role hierarchy - Add expandDiscordRoles() function to traverse both GitHub parent chain and discordImplies relationships when syncing Discord roles - SDK_MAINTAINERS now implies MAINTAINERS for Discord This ensures that users with SDK roles (GO_SDK, PYTHON_SDK, etc.) automatically get the 'maintainers (synced)' Discord role through: GO_SDK -> SDK_MAINTAINERS (via GitHub parent) -> MAINTAINERS (via discordImplies) Claude-Generated-By: Claude Code (cli/claude-opus-4-5=100%) Claude-Steers: 5 Claude-Permission-Prompts: 1 Claude-Escapes: 0 --- src/config/roles.ts | 7 +++++++ src/discord.ts | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/config/roles.ts b/src/config/roles.ts index 38515d0..a97d44b 100644 --- a/src/config/roles.ts +++ b/src/config/roles.ts @@ -38,6 +38,12 @@ export interface Role { github?: GitHubConfig; discord?: DiscordConfig; google?: GoogleConfig; + /** + * Roles that are implied for Discord membership. + * If a user has this role, they automatically get the implied roles' Discord roles too. + * This is separate from GitHub parent relationships and allows Discord-specific hierarchy. + */ + discordImplies?: readonly RoleId[]; } /** @@ -135,6 +141,7 @@ export const ROLES: readonly Role[] = [ description: 'Authors and maintainers of official MCP SDKs', github: { team: 'sdk-maintainers', parent: ROLE_IDS.STEERING_COMMITTEE }, discord: { role: 'sdk maintainers (synced)' }, + discordImplies: [ROLE_IDS.MAINTAINERS], // SDK maintainers are also general maintainers }, { id: ROLE_IDS.CSHARP_SDK, diff --git a/src/discord.ts b/src/discord.ts index 9ef4f43..d08f17a 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -398,6 +398,38 @@ const roleLookup = buildRoleLookup(); // Discord roles keyed by Discord role name const roles: Record = {}; +/** + * Expand a set of role IDs to include all implied Discord roles. + * This traverses: + * 1. GitHub parent relationships (e.g., GO_SDK -> SDK_MAINTAINERS) + * 2. discordImplies relationships (e.g., SDK_MAINTAINERS -> MAINTAINERS) + */ +function expandDiscordRoles(roleIds: readonly RoleId[]): Set { + const expanded = new Set(); + const toProcess = [...roleIds]; + + while (toProcess.length > 0) { + const roleId = toProcess.pop()!; + if (expanded.has(roleId)) continue; + expanded.add(roleId); + + const role = roleLookup.get(roleId); + if (!role) continue; + + // Follow GitHub parent relationship + if (role.github?.parent) { + toProcess.push(role.github.parent); + } + + // Follow discordImplies relationships + if (role.discordImplies) { + toProcess.push(...role.discordImplies); + } + } + + return expanded; +} + // Only create Discord resources if Discord is enabled if (DISCORD_ENABLED) { // These are guaranteed to be defined when DISCORD_ENABLED is true @@ -424,8 +456,11 @@ if (DISCORD_ENABLED) { MEMBERS.forEach((member) => { if (!member.discord) return; + // Expand roles to include parents and implied roles + const expandedRoleIds = expandDiscordRoles(member.memberOf); + // Get the Discord role IDs this member should have - const expectedRoleIds = member.memberOf + const expectedRoleIds = Array.from(expandedRoleIds) .map((roleId: RoleId) => { const role = roleLookup.get(roleId); if (!role?.discord) return null;