Skip to content

chore(lor-pivot): remove bans, timeouts, warden tier (phase 2)#56

Merged
BuckyMcYolo merged 2 commits into
mainfrom
lor-delete-pass
May 29, 2026
Merged

chore(lor-pivot): remove bans, timeouts, warden tier (phase 2)#56
BuckyMcYolo merged 2 commits into
mainfrom
lor-delete-pass

Conversation

@BuckyMcYolo
Copy link
Copy Markdown
Owner

@BuckyMcYolo BuckyMcYolo commented May 29, 2026

Strip ban / timeout features (Discord-style community moderation) and the warden tier from the granular better-auth permission system.

Keep the per-guild permission architecture intact — assertGuildPermission,
dynamic role grants via guild_role table, and the createAccessControl machinery all stay. The trims align the system with Lor's scope: workspace admin removes members (kick); no banishment, no timeouts, no moderator middle tier.

Schema:

  • Delete guild-bans.ts.
  • Drop communicationDisabledUntil/By/Reason columns from guild-members.
  • Change guild_member.role from text({enum}) to plain text() — follows better-auth's organization-member pattern, allows dynamic role names defined per-guild via guild_role table.

Permissions:

  • Drop announcement resource from the statement map (no announcement channels in Lor).
  • Drop ban and timeout actions from the guildMember statement.
  • Drop the warden role entirely (positions/labels/rate-limits collapse to owner/admin/member; assignableGuildRoles = ["admin", "member"]).
  • Rename "Citizen" label to "Member".
  • Delete isCommunicationDisabled / assertMemberCanCommunicate helpers in apps/api/src/lib/permissions.ts.

API:

  • Delete banGuildMember, timeoutGuildMember, clearGuildMemberTimeout handlers + routes + zod schemas + .openapi() mounts.
  • Strip communicationDisabled* fields from listGuildMembers selects and the toGuildMemberPresence shape.
  • Remove the guild-ban check from invites/handlers.ts.
  • Remove the communicationDisabledUntil check from uploads/handlers.ts.

Realtime:

  • Drop the communication-disabled gate from message:send.
  • rate-limit.ts falls back to member tier (30/min) for unknown custom role values (since role is now plain text).

Web:

  • Strip ban + timeout UI / mutations / dialogs from guild-members-panel.
  • Keep kick + role-edit UI (role select offers admin/member only).
  • Narrow authClient.organization.getActiveMember() return at the queryFn boundary in guild-header.tsx and channel-list.tsx — collapses the better-auth defensive index signature workaround down to clean { userId: string; role: string } | null.

PIVOT.md updated: granular perm system + dynamic roles stay (decision reversed mid-pass); only the feature-tied trims land.

Refs PIVOT.md "Delete aggressively" §banishment-and-timeouts.

This PR (phase 2 of the Lor pivot) removes Discord-style bans/timeouts and the warden moderator tier while preserving per-guild permissions. Changes touch schema, permissions, API routes, realtime services, web UI, and documentation.

Schema

  • Deleted packages/db/src/schemas/guild-bans.ts (removed guildBan table & relations).
  • Removed communicationDisabledUntil / communicationDisabledBy / communicationDisabledReason from guild_member schema; removed related relations.
  • Changed guild_member.role handling: added GUILD_MEMBER_ROLES = ["member", "admin"] and GuildMemberRole type; exported select/insert/update Zod schemas for guild members.
  • Removed guildBan relation from guilds and users schema exports and adjusted relation wiring.
  • Updated packages/db/src/schemas/index.ts to stop re-exporting guild-bans.

Permissions

  • API: removed isCommunicationDisabled() and assertMemberCanCommunicate() from apps/api/src/lib/permissions.ts.
  • Auth/package perms: removed warden role; roles now owner, admin, member. assignableGuildRoles limited to ["admin","member"]; guildRoleLabels/positions and per-role rate limits updated accordingly.
  • Web: replaced role-only helpers with member+guild authority/context; added isOwner/isAdmin/isAdminOrOwner; updated formatGuildRole to normalize non-guild roles to "Member"; updated channel and member management permission helpers (canManage/create/delete channels, canPinMessages, canSendInAnnouncement, canKickGuildMembers, canManageGuildMember). Removed ban/timeout-specific helpers.

API routes & schemas

  • Removed handlers: banGuildMember, timeoutGuildMember, clearGuildMemberTimeout (apps/api/src/routes/v1/guilds/handlers.ts) and their route definitions / types (routes.ts, index.ts).
  • Removed communicationDisabled* fields from guildMemberPresenceSchema and from listGuildMembers/selects and toGuildMemberPresence mapping (schema.ts, handlers.ts).
  • acceptInvite: removed pre-transaction "active ban" check.
  • uploads presign: refactored to use assertGuildPermission for announcement channels; removed communicationDisabledUntil selection.
  • OpenAPI wiring updated to reflect removed endpoints; kick/update role routes remain.

Realtime

  • AccessibleChannel type no longer exposes communicationDisabledUntil / communicationDisabledReason.
  • Removed assertChannelCommunicationAllowed timeout/communication-disabled gate from message operations (create/edit/delete/toggleReaction).
  • Announcement-channel send permission changed to require channel: ["create"] (message error updated).
  • Rate limiter: unknown custom guild role values now fall back to member tier (getRoleRateLimit(role) treats non-guild-role as member) instead of rejecting.

Web UI

  • guild-members-panel: removed ban/timeout UI and flows; retained role-edit (role select limited to admin/member) and kick. Moderation dialog simplified to kick-only. MemberRow props/signature updated and role label default normalized to "Member" (was "Citizen").
  • guild-header & channel-list: permission computation refactored to build permissionCtx from activeMember + guild ownerId and use new permission helpers (canKickGuildMembers, isAdminOrOwner, canCreate/Manage/DeleteChannels).
  • Channel view: replaced role-based checks with canPinMessages / canSendInAnnouncement using derived activeMemberCtx.

Docs

  • PIVOT.md updated to list deletions and the preserved per-guild permission architecture (references deletion of ban/timeout and warden removal).

Files changed (representative)

  • apps/api: routes/v1/guilds/{handlers,index,routes,schema}.ts, routes/v1/invites/handlers.ts, routes/v1/uploads/handlers.ts, lib/permissions.ts
  • apps/realtime: services/channel-access.ts, services/messages.ts, services/rate-limit.ts
  • apps/web: several components under src/components, src/lib/permissions.ts, routes/_authenticated
  • packages/auth: src/lib/{auth-client,auth,permissions}.ts
  • packages/db: src/schemas/{guild-bans.ts, guild-members.ts, guilds.ts, index.ts, users.ts}

Confidence Score: 2/5

Rationale: The refactor is broad and appears internally consistent across API, realtime, web, auth, and schema definitions. However, there is a critical deployment gap: no database migration files were found in the repo search to remove the guild_member communicationDisabled columns or drop the guild_bans table. Removing schema columns/tables requires explicit migrations; absent these, deploying will break or risk data loss. Addressing this missing migration (and adding tests or CI checks to catch lingering references) is required before merging. Other non-blocking items to consider: update any external tooling or scripts that relied on the removed fields, and add a short dev note documenting the owner-via-ownerId vs. role-based owner distinction to avoid future confusion.

Review Change Stack

Strip ban / timeout features (Discord-style community moderation) and
the warden tier from the granular better-auth permission system.

Keep the per-guild permission architecture intact —
`assertGuildPermission`,
dynamic role grants via guild_role table, and the `createAccessControl`
machinery all stay. The trims align the system with Lor's scope:
workspace admin removes members (kick); no banishment, no timeouts, no
moderator middle tier.

Schema:
- Delete guild-bans.ts.
- Drop communicationDisabledUntil/By/Reason columns from guild-members.
- Change guild_member.role from text({enum}) to plain text() — follows
  better-auth's organization-member pattern, allows dynamic role names
  defined per-guild via guild_role table.

Permissions:
- Drop `announcement` resource from the statement map (no announcement
  channels in Lor).
- Drop `ban` and `timeout` actions from the guildMember statement.
- Drop the warden role entirely (positions/labels/rate-limits collapse
  to
  owner/admin/member; assignableGuildRoles = ["admin", "member"]).
- Rename "Citizen" label to "Member".
- Delete isCommunicationDisabled / assertMemberCanCommunicate helpers
  in apps/api/src/lib/permissions.ts.

API:
- Delete banGuildMember, timeoutGuildMember, clearGuildMemberTimeout
  handlers + routes + zod schemas + .openapi() mounts.
- Strip communicationDisabled* fields from listGuildMembers selects and
  the toGuildMemberPresence shape.
- Remove the guild-ban check from invites/handlers.ts.
- Remove the communicationDisabledUntil check from uploads/handlers.ts.

Realtime:
- Drop the communication-disabled gate from message:send.
- rate-limit.ts falls back to member tier (30/min) for unknown custom
  role values (since role is now plain text).

Web:
- Strip ban + timeout UI / mutations / dialogs from guild-members-panel.
- Keep kick + role-edit UI (role select offers admin/member only).
- Narrow `authClient.organization.getActiveMember()` return at the
  queryFn
  boundary in guild-header.tsx and channel-list.tsx — collapses the
  better-auth defensive index signature workaround down to clean
  `{ userId: string; role: string } | null`.

PIVOT.md updated: granular perm system + dynamic roles stay (decision
reversed mid-pass); only the feature-tied trims land.

Refs PIVOT.md "Delete aggressively" §banishment-and-timeouts.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 28894ac5-3ee0-4908-91c1-6fe38ff2f287

📥 Commits

Reviewing files that changed from the base of the PR and between 1aa240f and 0036e42.

📒 Files selected for processing (1)
  • apps/api/src/routes/v1/guilds/schema.ts

📝 Walkthrough

Walkthrough

This PR removes guild ban/timeout features and the "warden" role, removes communication-disabled fields and guild bans schema, refactors permission checks from role-only to member+guild-based across API, realtime services, rate-limiting, and web UI, and updates auth and docs to the final three-role model (owner/admin/member).

Changes

Remove Banishment & Timeouts, Simplify Permission Model

Layer / File(s) Summary
Database schema: delete bans and communication fields
packages/db/src/schemas/guild-members.ts, packages/db/src/schemas/guilds.ts, packages/db/src/schemas/users.ts, packages/db/src/schemas/index.ts
Guild bans table and communication-disabled columns (communicationDisabledUntil, communicationDisabledBy, communicationDisabledReason) removed; exports and relations simplified; GUILD_MEMBER_ROLES and GuildMemberRole constants/types added; Zod select/insert/update schemas exported.
Auth configuration: remove warden role and update access control
packages/auth/src/lib/auth-client.ts, packages/auth/src/lib/auth.ts, packages/auth/src/lib/permissions.ts
warden role removed from auth client and server config; access-control statements and role metadata updated to only owner, admin, member; assignable roles/rate-limits adjusted.
API route definitions and schemas
apps/api/src/routes/v1/guilds/routes.ts, apps/api/src/routes/v1/guilds/schema.ts
Ban/timeout/clear-timeout route definitions and schema imports deleted; guildMemberPresenceSchema no longer includes communication-disabled fields; moderate/member-role schemas reordered.
API route wiring and guild member handlers
apps/api/src/routes/v1/guilds/index.ts, apps/api/src/routes/v1/guilds/handlers.ts
Guild router registrations updated to remove ban/timeout endpoints; presence mapping and DB selects stop returning communication-disabled fields; ban/timeout handler implementations removed.
Server-side permission & realtime cleanup
apps/api/src/lib/permissions.ts, apps/api/src/routes/v1/invites/handlers.ts, apps/api/src/routes/v1/uploads/handlers.ts, apps/realtime/src/services/channel-access.ts, apps/realtime/src/services/messages.ts, apps/realtime/src/services/rate-limit.ts
Communication-timeout helpers removed from API permissions; active-ban precheck removed from invite acceptance; uploads presign now uses assertGuildPermission; communication-disabled guards removed from channel-access and message flows; rate limiter now maps unknown roles to member tier.
Web permissions library: refactor to member+guild-based model
apps/web/src/lib/permissions.ts
Permission helpers rewritten to accept member+guild context; isOwner/isAdmin/isAdminOrOwner added; formatGuildRole accepts string and defaults to "Member"; channel/message/member-management checks updated; canPinMessages introduced; announcement posting gates use channel:create.
Web UI: integrate new permission model and remove moderation UI
apps/web/src/components/sidebar/channel-panel/channel-list.tsx, apps/web/src/components/sidebar/channel-panel/guild-header.tsx, apps/web/src/components/sidebar/right-panel/guild-members-panel.tsx, apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
ChannelList and GuildHeader build permission context from activeMember + guild owner ID; GuildMembersPanel and MemberRow simplified to only support role updates and kicking (ban/timeout UI removed); ChannelView uses new permission helpers for pin/announcement gating.
Documentation: update migration plan
PIVOT.md
Migration plan explicitly documents deletion of ban/timeout schema and fields, removal of warden role, renaming default label to Member, and finalizing owner/admin/member model while keeping per-guild permissions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • BuckyMcYolo/lor#50: Updates announcement posting authorization and canSendInAnnouncement gating that overlaps with announcement permission changes here.

🐰 Whiskers' Review Sonnet

A guild's dark dungeons fade to dust,
The warden role bids its farewell,
Now members speak with open trust—
Three roles to ring the welcome bell! 🔔

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chore(lor-pivot): remove bans, timeouts, warden tier (phase 2)' directly and clearly summarizes the main changes: removal of ban functionality, timeout features, and the warden moderator tier across the codebase as part of the LOR migration plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lor-delete-pass

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx (1)

338-338: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Outdated reference to "wardens" in disabled message.

The disabledReason string still mentions "wardens" but the warden role has been removed in this PR. This should be updated to reflect the new permission model.

Proposed fix
-        disabledReason="Only owners, admins, and wardens can post in decree channels"
+        disabledReason="Only owners and admins can post in decree channels"

Note: The relevant code snippet from apps/web/src/components/sidebar/channel-panel/create-channel-dialog.tsx (lines 36-37) also contains the text "Only admins and wardens can post" which should be updated separately.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/_authenticated/`$guildSlug/$channelId.tsx at line 338,
Update the stale "wardens" text to match the new permission model: change the
disabledReason prop value currently set to "Only owners, admins, and wardens can
post in decree channels" to remove "wardens" (e.g. "Only owners and admins can
post in decree channels"), and also update the user-facing text inside the
CreateChannelDialog component (the string "Only admins and wardens can post") to
the corresponding new wording ("Only admins can post" or "Only owners and admins
can post" as appropriate).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/routes/v1/guilds/schema.ts`:
- Around line 48-50: The schema updateGuildMemberRoleRequestSchema currently
hardcodes the allowed roles; instead import the authoritative
assignableGuildRoles (from packages/auth/src/lib/permissions.ts) and use it to
build the z.enum so the schema derives from a single source of truth (e.g.,
z.enum(assignableGuildRoles as const) or convert the imported array into the
required readonly tuple before passing to z.enum); update the import and replace
the hardcoded ["admin","member"] with the derived value so future changes to
assignableGuildRoles automatically flow to updateGuildMemberRoleRequestSchema.

In `@PIVOT.md`:
- Line 270: Update the UI to remove stale "Decree"/"wardens" terminology: remove
the "Decree" channel option in the CreateChannelDialog component
(apps/web/src/components/sidebar/channel-panel/create-channel-dialog.tsx) so it
no longer renders that option or its wardens-focused copy, and change the
disabledReason string in the authenticated guild route
(apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx) from "Only
owners, admins, and wardens can post in decree channels" to wording that matches
the new role model (e.g., "Only owners and admins can post in this channel" or
reference the dynamic guild_role model); ensure any UI labels/copy referencing
"wardens" or "Decree" are removed or replaced consistently.

---

Outside diff comments:
In `@apps/web/src/routes/_authenticated/`$guildSlug/$channelId.tsx:
- Line 338: Update the stale "wardens" text to match the new permission model:
change the disabledReason prop value currently set to "Only owners, admins, and
wardens can post in decree channels" to remove "wardens" (e.g. "Only owners and
admins can post in decree channels"), and also update the user-facing text
inside the CreateChannelDialog component (the string "Only admins and wardens
can post") to the corresponding new wording ("Only admins can post" or "Only
owners and admins can post" as appropriate).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0f30ed17-21aa-4679-aec9-f93f39d5a807

📥 Commits

Reviewing files that changed from the base of the PR and between e065918 and 1aa240f.

📒 Files selected for processing (24)
  • PIVOT.md
  • apps/api/src/lib/permissions.ts
  • apps/api/src/routes/v1/guilds/handlers.ts
  • apps/api/src/routes/v1/guilds/index.ts
  • apps/api/src/routes/v1/guilds/routes.ts
  • apps/api/src/routes/v1/guilds/schema.ts
  • apps/api/src/routes/v1/invites/handlers.ts
  • apps/api/src/routes/v1/uploads/handlers.ts
  • apps/realtime/src/services/channel-access.ts
  • apps/realtime/src/services/messages.ts
  • apps/realtime/src/services/rate-limit.ts
  • apps/web/src/components/sidebar/channel-panel/channel-list.tsx
  • apps/web/src/components/sidebar/channel-panel/guild-header.tsx
  • apps/web/src/components/sidebar/right-panel/guild-members-panel.tsx
  • apps/web/src/lib/permissions.ts
  • apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
  • packages/auth/src/lib/auth-client.ts
  • packages/auth/src/lib/auth.ts
  • packages/auth/src/lib/permissions.ts
  • packages/db/src/schemas/guild-bans.ts
  • packages/db/src/schemas/guild-members.ts
  • packages/db/src/schemas/guilds.ts
  • packages/db/src/schemas/index.ts
  • packages/db/src/schemas/users.ts
💤 Files with no reviewable changes (8)
  • packages/db/src/schemas/index.ts
  • packages/db/src/schemas/guild-bans.ts
  • apps/api/src/routes/v1/invites/handlers.ts
  • apps/realtime/src/services/channel-access.ts
  • apps/api/src/lib/permissions.ts
  • packages/auth/src/lib/auth.ts
  • apps/api/src/routes/v1/guilds/index.ts
  • apps/api/src/routes/v1/guilds/handlers.ts

Comment thread apps/api/src/routes/v1/guilds/schema.ts
Comment thread PIVOT.md
@BuckyMcYolo BuckyMcYolo merged commit 0094cf9 into main May 29, 2026
2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 29, 2026
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.

1 participant