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
This commit is contained in:
David Soria Parra 2026-01-15 14:16:06 +00:00
commit a667c4306a
No known key found for this signature in database
2 changed files with 43 additions and 1 deletions

View file

@ -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,

View file

@ -398,6 +398,38 @@ const roleLookup = buildRoleLookup();
// Discord roles keyed by Discord role name
const roles: Record<string, DiscordRole> = {};
/**
* 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<RoleId> {
const expanded = new Set<RoleId>();
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;