1b30983307
## Summary Closes #21229. The two AI role permissions behaved **opposite to their labels**. The trap is that the flag's code name is the inverse of its UI label: | `PermissionFlagType` | UI label | Section | Means | |---|---|---|---| | `AI` | **"Ask AI"** | Actions | End-user: chat with AI | | `AI_SETTINGS` | **"AI"** | Member / settings | Admin: configure AI agents | Before this PR (on `main`): - `AI` ("Ask AI", chat) gated **both** the AI chat **and** the AI settings page. - `AI_SETTINGS` ("AI", configure agents) gated **nothing** the user could see. So a chat-only user could reach the whole AI **configuration** page, and toggling the "AI" settings permission did nothing — exactly the misalignment reported in #21229. ## Root cause `PermissionFlagType.AI` *reads* like "the AI permission", so it looks like the natural gate for the AI settings page — but it's actually the **chat** flag. The settings page (nav item + route) had been pointed at `AI` in #21072 to match the Overview stats query (`findWorkspaceAiStats`), which was itself mis-gated on `AI`. Both the stats query and the rest of the settings surface are admin/config features, so they belong on `AI_SETTINGS`. ## Changes All three move the **AI settings surface** from the chat flag (`AI`) to the settings flag (`AI_SETTINGS`); chat keeps following `AI`: - `useSettingsNavigationItems.tsx` — AI nav item → `AI_SETTINGS` - `SettingsRoutes.tsx` — AI settings route group → `AI_SETTINGS` - `ai-workspace-stats.resolver.ts` — `findWorkspaceAiStats` (settings-only, drives the Overview tab) → `AI_SETTINGS` After this: the "AI" permission controls the AI settings page + its Overview; the "Ask AI" permission controls the chat. Both toggles now match their labels. ## Test plan - [ ] Role with **only "Ask AI"** (`AI`): AI chat tabs/pane visible; **Settings → AI is hidden** and the route is not reachable. - [ ] Role with **only "AI"** (`AI_SETTINGS`): Settings → AI is visible, Overview stats load; chat nav is hidden. - [ ] Admin (both flags): everything works as before. ## Known follow-ups (out of scope — pre-existing, shared endpoints) These remain on `AI` because they're shared with non-settings surfaces and need either OR-gating or a resolver split, so a role with `AI_SETTINGS` but **not** `AI` still can't use them yet: - `getAiSystemPromptPreview` (Models/Prompts tabs) lives in the chat resolver, class-gated `AI`; NestJS guards are additive so it can't be cleanly method-overridden — it should be pulled into a settings resolver. - Agent reads `findManyAgents` / `findOneAgent` (agent create/edit forms) are class-gated `AI` and shared with the **Workflow** editor and **Roles** pages; these want a guard that accepts `AI ∨ AI_SETTINGS ∨ WORKFLOWS`.