feat(ai): add 'Who can use AI' audience setting, default Everyone (LC-702) #668

Merged
longjacksonle merged 1 commit from feat/LC-702-ai-audience-toggle into main 2026-08-13 04:15:02 +02:00

What

Adds a second admin setting, Who can use AI (settings.llm_audience), next to the existing enable toggle in Admin > Settings > AI features.

  • Everyone (new default): any logged-in member sees and uses AI in the rooms and DMs they belong to.
  • Staff only: preserves LC-679's original scope (site admins, enclave owners/admins, room moderators) for piloting the surface before opening it up.

Why

LC-679 shipped the AI flag as a staged-rollout control: once an admin turned AI on, the whole surface (writing assistant, catch-up, /ask, translate, suggested replies, semantic search, transcript summaries) stayed scoped to staff. A regular member saw no AI affordances and got a 403 on a direct request. LC-702 asks why regular users still cannot use AI once the flag is on. The default is now Everyone, so flipping the flag opens the surface to all members; Staff only stays available for pre-rollout piloting.

How

  • New live-read setting llm_audience in ai_gate (absent or any non-"staff" value reads as "everyone", so a fresh deployment is open by default the moment the flag flips on).
  • New predicate ai_gate::allowed_in_room = audience_is_everyone || privileged_in_room. Every render path (room, DM, thread panel, room header, slash menu, transcript page) and every route guard (require_llm_in_room, require_embeddings_in_room, can_use_embeddings_in_room) now asks allowed_in_room instead of privileged_in_room. The llm_enabled flag stays the separate kill switch.
  • Admin UI: an audience radio group folded into the existing "Save AI setting" form (post_llm_flag persists both the toggle and the audience in one save), with copy updated to describe the audience instead of the old staff-only wording. Authored .lc-radio CSS in main.css. English + Spanish locale parity.

Security

Opening the audience does not expose non-members. Every AI route already runs an independent room-access check (is_room_accessible / require_access) before the gate, and /ask is reachable only via a slash command posted into a room the user already has access to. The audience widens access only within a room the user already belongs to.

Testing

  • routes_ai_gate integration tests updated: flag off -> 403 even for admin; flag on + default "everyone" -> plain member allowed (200); flag on + "staff" -> member 403 while admin passes; absent flag defaults off.
  • Full suite green: just test (195 binaries, 1636 tests), clippy standalone + saas, cargo fmt --check, i18n en/es parity.

Closes LC-702.

🤖 Generated with Claude Code

https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy

## What Adds a second admin setting, **Who can use AI** (`settings.llm_audience`), next to the existing enable toggle in Admin > Settings > AI features. - **Everyone** (new default): any logged-in member sees and uses AI in the rooms and DMs they belong to. - **Staff only**: preserves LC-679's original scope (site admins, enclave owners/admins, room moderators) for piloting the surface before opening it up. ## Why LC-679 shipped the AI flag as a staged-rollout control: once an admin turned AI on, the whole surface (writing assistant, catch-up, /ask, translate, suggested replies, semantic search, transcript summaries) stayed scoped to staff. A regular member saw no AI affordances and got a 403 on a direct request. LC-702 asks why regular users still cannot use AI once the flag is on. The default is now Everyone, so flipping the flag opens the surface to all members; Staff only stays available for pre-rollout piloting. ## How - New live-read setting `llm_audience` in `ai_gate` (absent or any non-`"staff"` value reads as `"everyone"`, so a fresh deployment is open by default the moment the flag flips on). - New predicate `ai_gate::allowed_in_room` = `audience_is_everyone || privileged_in_room`. Every render path (room, DM, thread panel, room header, slash menu, transcript page) and every route guard (`require_llm_in_room`, `require_embeddings_in_room`, `can_use_embeddings_in_room`) now asks `allowed_in_room` instead of `privileged_in_room`. The `llm_enabled` flag stays the separate kill switch. - Admin UI: an audience radio group folded into the existing "Save AI setting" form (`post_llm_flag` persists both the toggle and the audience in one save), with copy updated to describe the audience instead of the old staff-only wording. Authored `.lc-radio` CSS in `main.css`. English + Spanish locale parity. ## Security Opening the audience does not expose non-members. Every AI route already runs an independent room-access check (`is_room_accessible` / `require_access`) before the gate, and `/ask` is reachable only via a slash command posted into a room the user already has access to. The audience widens access only within a room the user already belongs to. ## Testing - `routes_ai_gate` integration tests updated: flag off -> 403 even for admin; flag on + default "everyone" -> plain member allowed (200); flag on + "staff" -> member 403 while admin passes; absent flag defaults off. - Full suite green: `just test` (195 binaries, 1636 tests), clippy standalone + saas, `cargo fmt --check`, i18n en/es parity. Closes LC-702. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy
feat(ai): add "Who can use AI" audience setting, default Everyone (LC-702)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / TruffleHog (push) Successful in 6s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 6s
check-secrets / TruffleHog (pull_request) Successful in 6s
Check / clippy + fmt + tests (pull_request) Successful in 5m32s
Create release / Create release from merged PR (pull_request) Has been skipped
d88724c563
LC-679 shipped the AI feature flag as a staged-rollout control: when an admin turned AI on, the whole surface (writing assistant, catch-up, /ask, translate, suggested replies, semantic search, transcript summaries) stayed scoped to staff (site admins, enclave owners/admins, room moderators). Regular members saw no AI affordances and got a 403 on a direct request, which is what LC-702 reports.

This adds a second admin setting, "Who can use AI" (settings.llm_audience), next to the existing enable toggle. "Everyone" (the new default) admits any logged-in member in the rooms and DMs they belong to; "Staff only" keeps LC-679's original scope for piloting the surface before opening it up. The llm_enabled flag stays the separate kill switch.

Implementation: a new live-read setting llm_audience (absent or any non-"staff" value reads as "everyone", so a fresh deployment is open by default the moment the flag flips on) and a new predicate ai_gate::allowed_in_room = audience_is_everyone || privileged_in_room. Every render path (room, DM, thread panel, room header, slash menu, transcript page) and every route guard (require_llm_in_room, require_embeddings_in_room, can_use_embeddings_in_room) now asks allowed_in_room instead of privileged_in_room. The admin UI folds an audience radio group into the existing "Save AI setting" form, with copy updated to describe the audience; English + Spanish locale parity.

Opening the audience does not expose non-members: every AI route already runs an independent room-access check (is_room_accessible / require_access) before the gate, and /ask is reachable only via a slash command posted into a room the user already has access to, so the audience widens access only within a room the user already belongs to.

Verification: routes_ai_gate integration tests updated (flag off -> 403 even for admin; flag on + default "everyone" -> plain member allowed 200; flag on + "staff" -> member 403 while admin passes; absent flag defaults off). Full suite green: just test (195 binaries, 1636 tests), clippy standalone + saas, cargo fmt --check, i18n en/es parity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ufqbuN7mjyt4ms5NEygMy
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-13 04:10:01 +02:00
longjacksonle deleted branch feat/LC-702-ai-audience-toggle 2026-08-13 04:15:03 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!668
No description provided.