Skip to content

Harden Play channel against category moves; add /party reset - #65

Merged
diese-tech merged 2 commits into
mainfrom
claude/party-setup-permission-hardening
Aug 21, 2026
Merged

Harden Play channel against category moves; add /party reset#65
diese-tech merged 2 commits into
mainfrom
claude/party-setup-permission-hardening

Conversation

@diese-tech

Copy link
Copy Markdown
Owner

Summary

Prompted by a real incident: an admin ran /party setup, then moved #godforge-play into a different category, and it silently became inaccessible — Discord's audit log showed it as No Access — with no way to recover other than manual server surgery. Root-caused via the audit log: the channel wasn't deleted, it was created with zero permission overwrites of its own, so its visibility was purely inherited/ambient and a routine reorganization silently stranded it.

This branched off main directly (not off #64) since the owner asked for it to be scoped separately from that PR's already-frozen queue-UX changes.

Player-facing UX changes

None — this only affects guild administration.

Organizer-facing UX changes

  • create_play_channel() now gives the bot an explicit channel-level permission overwrite (View Channel, Send Messages, Embed Links, Read Message History) at creation time. A plain drag-to-a-new-category move doesn't touch a channel's own overwrites unless someone explicitly syncs permissions, so this survives the exact scenario that broke it. Not bulletproof against a deliberate permission sync, but it closes the common accidental case.
  • New /party reset command: deletes every Discord resource /party setup is currently tracking (Play channel, room category, managed cosmetic roles) and clears the stored configuration, so a follow-up /party setup starts clean instead of hitting "channel already exists" conflicts from orphaned resources.
    • Requires Manage Server, same gate as /party setup.
    • Defaults to a dry-run preview listing exactly what's currently stored and what would be deleted; only deletes anything when called with confirm:True.
    • Partial deletion failures (e.g. the exact permission loss that caused this incident) are reported per-resource but don't block clearing the stored config, so the admin isn't left stuck a second time.

Lifecycle / state changes

None to the party/queue domain model. /party reset only touches the managed block of per-guild settings (playChannelId, playMessageId, roomCategoryId, roleIds) — testMode and all unrelated settings are left untouched.

Schema / migration changes

None.

Tests

12 new/updated tests in tests/unit/test_party_setup_command_characterization.py:

  • the Play channel is created with a bot-self permission overwrite
  • /party reset requires a guild context and Manage Server
  • reporting "nothing stored" when there's nothing to reset
  • dry-run preview lists resources without deleting or clearing anything
  • confirmed reset deletes channel/category/roles and clears all stored IDs, correctly handling a resource that's already gone (e.g. a role deleted out-of-band)
  • a deletion failure (simulated discord.DiscordException) is reported but stored config is still cleared

Full suite: 618 passing (was 611 on main).

Implementation interpretations made

  • "Reset" deletes the actual Discord resources, not just the stored IDs — clearing IDs alone wouldn't be enough to unblock a clean re-setup, since create_play_channel()/ensure_room_category() both reject creation if a channel/category with the same name already exists (to avoid silently adopting an unrelated resource). Actually deleting the orphaned resources is what makes /party reset/party setup work end-to-end without manual cleanup.
  • Chose an explicit confirm:True flag over a Discord confirm-button UI for the destructive step — simpler, and consistent with this being a rare admin-recovery command rather than a normal-operation surface.

Known limitations

  • The bot's own overwrite won't survive someone deliberately clicking "Sync Permissions" on the destination category — that's an explicit, intentional action that overrides all of a channel's own overwrites, and there's no way to protect against a deliberate choice like that from the channel-creation side.
  • /party reset doesn't touch active match rooms under the "GodForge Rooms" category (a separate lifecycle, governed by match_room_repository) — it only clears what /party setup itself created (Play channel/panel, room category, cosmetic roles).

Manual Discord test checklist

  1. Run /party setup in a fresh server. Confirm #godforge-play is created. In the channel's permission settings, confirm there's now an explicit member-level overwrite for the GodForge bot (View Channel, Send Messages, Embed Links, Read Message History), separate from any role-based permissions.
  2. Move #godforge-play into a different category via drag-and-drop without clicking "Sync Permissions". Confirm the bot's Play panel still works normally (buttons respond, channel stays visible) — this is the regression this PR fixes.
  3. As a non-admin (no Manage Server), run /party reset — confirm it's rejected.
  4. As an admin, run /party reset with no confirm argument (or confirm:False). Confirm it lists the currently-configured Play channel, room category, and roles, and says nothing was changed. Verify in Discord that nothing was actually deleted.
  5. Run /party reset confirm:True. Confirm the Play channel, room category, and all GodForge-managed roles are deleted from the server, and the response says what was deleted.
  6. Run /party setup again immediately after. Confirm it creates a brand-new channel/category/roles with no "already exists" conflict errors.
  7. Simulate the partial-failure path: manually revoke the bot's Manage Channels on one managed role or channel, then run /party reset confirm:True — confirm it reports that specific deletion failure but still clears the stored config, and that /party setup afterward isn't blocked by the stale (now-cleared) reference.

🤖 Generated with Claude Code

https://claude.ai/code/session_014RF4jAhbRnZzcgG11SXarx


Generated by Claude Code

Prompted by a real incident: an admin ran /party setup, then moved
#godforge-play into a different category, and it silently became
inaccessible (Discord's audit log showed it as "No Access") with no way to
recover other than manual server surgery.

- create_play_channel() now gives the bot an explicit channel-level
  permission overwrite (View Channel, Send Messages, Embed Links, Read
  Message History) at creation time. A plain drag-to-a-new-category move
  doesn't touch a channel's own overwrites unless someone explicitly syncs
  permissions, so this survives the exact scenario that broke it — it's
  not bulletproof against a deliberate permission sync, but it closes the
  common accidental case.
- New /party reset command: deletes every Discord resource /party setup is
  currently tracking (Play channel, room category, managed cosmetic roles)
  and clears the stored configuration, so a follow-up /party setup starts
  clean instead of hitting "channel already exists" conflicts from
  orphaned resources. Defaults to a dry-run preview; only deletes anything
  when called with confirm:True. Requires Manage Server, same as setup.
  Partial deletion failures (e.g. the exact permission loss that caused
  this) are reported but don't block clearing the stored config, so the
  admin isn't left stuck a second time.

12 new/updated tests. Full suite: 653 passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RF4jAhbRnZzcgG11SXarx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dd73df0a8f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread utils/party_setup_command.py Outdated
…w ones

Codex review on PR #65 (P1) pointed out that _resolve_channel reuses a
stored Play channel without ever calling create_play_channel, so the
bot-self overwrite added there only protects deployments that run
/party setup for the first time after this fix ships. Every existing
guild's Play channel stays exactly as vulnerable to the category-move
bug as before, and would need the new destructive /party reset just to
pick up the fix.

GuildSetupService.reconcile() now calls a new
ensure_channel_overwrite() operation whenever it reuses a stored
channel (skipped when the channel was just created, since creation
already applies it atomically). The Discord adapter repairs the
overwrite via channel.set_permissions() and treats failure as
best-effort — the existing _panel_permission_failure() check still
catches and reports any actual, current loss of visibility.
@diese-tech
diese-tech merged commit abf1e26 into main Aug 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants