Reviewed member-role changes
Member-role changes have no immediate-call path. Set capabilities.memberRoleChanges: true, list every eligible guild in scopes.memberRoleGuildIds, list every assignable role in scopes.memberRoleIds, and place every ineligible operator, service account, and break-glass identity in scopes.protectedUserIds. The guild allowlist must be a subset of readScope.guildIds when the read allowlist exists. Enabling changes adds those guilds to the shared nonprivileged Gateway layout union and requires pinned application and bot IDs. The role allowlist is an explicit statement of operator intent, independent of the bot's Discord permissions, member-directory scope, moderation scope, role-creation scope, and permission-overwrite scope.
- Call
plan_member_role_changewith exact guild, user, and role IDs, theaddorremoveaction, a Discord audit-log reason, and a unique one-shot operation key. - Review the verified application and bot identities, exact target and selected role, channel evidence, role sets before and after, guild permission sets and delta, selected-role permissions, bot and target hierarchy, high-risk and unknown-bit warnings, every changed direct-channel permission decision, impact bounds, audit reason, operation-key hash, and keyed digest.
- Call
execute_member_role_changewith identical intent plus the digest. - Approve the signed MCP confirmation only if every identity, role transition, permission effect, warning, reason, hash, and digest remains intended.
- Review the exact readback state, activity ID, and outcome before any related operation.
Planning brackets the bounded Discord reads with identical complete Gateway layouts and accepts only a complete HTTP channel inventory or the exact non-obfuscated subset. It discards metadata for every obfuscated channel, then requires zero obfuscated channels for either role addition or removal because hidden permission overwrites would make a whole-guild impact proof incomplete. It also verifies the exact guild and owner, bot member, target member, and complete bounded role inventory. The selected role must be a standard non-managed role other than @everyone, and both the role and target member must be strictly below the bot's unique highest role. The target cannot be the bot, guild owner, a protected user, a pending membership-screening member, or an actively timed-out member. Layout discontinuity, arbitrary HTTP omission, ambiguous hierarchy, unresolved member roles, malformed overwrites, missing MANAGE_ROLES, unsupported channel evidence, and incomplete permission evaluation fail closed.
An addition rejects ADMINISTRATOR, unknown future permission bits on the selected role or its direct-channel overwrites, selected-role guild permissions outside the bot's complete effective guild permission set, and every selected-role channel overwrite allowance or effective channel-permission gain the bot does not itself hold in that exact channel. Known non-channel permission bits in any channel overwrite are malformed evidence and fail closed. A removal may revoke a role carrying high-risk or unknown permissions because that operation removes role-derived authority, but the plan calls out those properties. Every plan shows the target's before-and-after effective guild permissions and exact named delta, separately calls out high-risk effective gains from either role bits or channel overwrites, discloses unknown permission bits elsewhere in the complete role and direct-channel overwrite inventories, and compares the target's named effective permissions before and after across every supported direct guild channel. If the changed-channel set exceeds the bounded result limit, planning fails instead of truncating the approval preview.
Discord's guild-channel inventory does not include active threads. Each plan therefore states that inherited or membership-specific thread access is outside its direct-channel proof. External role, channel, timeout, or membership changes after planning invalidate the fresh digest where they affect the bound evidence, but Discord offers no conditional role-assignment primitive for the narrow interval after the final read.
An already present add or already absent remove is a verified no-op that needs no confirmation, reservation, activity record, or Discord write. A real execution atomically reserves the operation-key hash, appends pending content-free activity, performs one exact non-retried PUT or DELETE, and fetches the exact member to verify both the selected role state and the complete proposed role snapshot. A concurrent unrelated role change therefore produces completed-with-drift even when the selected role reached its intended state. The workflow never replaces the full role array, retries, rolls back, or infers a member or role from a display name.
The raw operation key, member and role names, channel names, permission evidence, audit reason, and Discord payloads never enter the durable activity or receipt records. A reserved key remains spent after rejection, uncertainty, drift, or local recording failure. Same-member changes serialize inside one process as defense in depth. The production facade additionally acquires durable exact member-and-role claims, so connector processes sharing the activity-state root exclude overlapping member-role changes and retain the claims after uncertainty. Inspect the exact member and Discord audit log before deciding whether a new intent should use a new operation key.
Canonical source: docs/reference.md
Documentation generated for guildcontrol@0.0.0. Canonical source and edit history remain in the public repository. GuildControl is an independent project and is not affiliated with or endorsed by Discord Inc. Discord is used only to identify the platform that GuildControl connects to.