一键导入
add-permission
// Add a new permission end-to-end — server constant + endpoint gate, and (admin app) mirror it into the permissions catalog + route guard. Use when a new endpoint needs authorization. See modules/identity.md + frontend/admin.md.
// Add a new permission end-to-end — server constant + endpoint gate, and (admin app) mirror it into the permissions catalog + route guard. Use when a new endpoint needs authorization. See modules/identity.md + frontend/admin.md.
Add a domain entity/aggregate with EF configuration and a migration to an existing FSH module. Use when adding a new database-backed entity. Pairs with add-feature and create-migration.
Add a vertical-slice feature (command/query + handler + validator + endpoint) to an existing FSH module. Use when adding an API endpoint or business operation to a module that already exists.
Build a capability end-to-end — backend vertical slice (Contracts→handler→validator→endpoint) AND the React page wired to it. Use when delivering a user-facing feature across API + UI. Composes add-feature + add-react-page.
Publish a cross-module integration event via the Outbox and handle it idempotently in another module. Use when one module must react to something that happened in another. See .agents/rules/eventing.md.
Create a new module (bounded context) — runtime + Contracts projects, IModule, DbContext, permissions, migrations, and the four registration sites. Use when adding a distinct business domain. For a feature in an existing module, use add-feature.
Add a list+create page to a React app (clients/admin or clients/dashboard) — API module, page, lazy route, (admin) permission gate, Playwright test. Use when adding any frontend screen. See .agents/rules/frontend/.
| name | add-permission |
| description | Add a new permission end-to-end — server constant + endpoint gate, and (admin app) mirror it into the permissions catalog + route guard. Use when a new endpoint needs authorization. See modules/identity.md + frontend/admin.md. |
A permission spans server + the admin app. The dashboard app does not mirror permissions — it reads them from the JWT and relies on the server's 403.
Modules.{X}.Contracts/Authorization/{X}Permissions.cs)Add the constant to the resource group and ensure it's in the module's All collection. Convention:
Permissions.{Resource}.{Action}.
public static class {X}Permissions
{
public static class {Resources}
{
public const string View = "Permissions.{Resources}.View";
public const string Create = "Permissions.{Resources}.Create"; // ← new
}
public static IReadOnlyList<FshPermission> All { get; } = [ /* … include the new one … */ ];
}
The module already calls PermissionConstants.Register({X}Permissions.All) in ConfigureServices, so a new entry in All is picked up automatically.
.RequirePermission({X}Permissions.{Resources}.Create);
⚠️ RequiredPermissionAttribute implements IRequiredPermissionMetadata. Never let a second/duplicate of that interface exist — it silently disables all .RequirePermission() gates app-wide. (See .agents/rules/modules/identity.md.)
clients/admin/src/lib/permissions.ts — add the matching string to the frozen tree (no runtime catalog endpoint exists; mirror by hand):
export const {Module}Permissions = Object.freeze({
{Resources}: { View: "Permissions.{Resources}.View", Create: "Permissions.{Resources}.Create" },
} as const);
If it should appear in the Role editor UI, add a PERMISSION_CATALOG entry ({ name, description, root?, basic? } under the right category group).
{ path: "{resources}/new",
element: <RouteGuard perms={[{Module}Permissions.{Resources}.Create]}><Create{Resource}Page /></RouteGuard> },
So RouteGuard passes on first paint, add the new permission to the test seed set (ADMIN_PERMS in clients/admin/tests/helpers/shell-mocks.ts, used by seedAuthedSession).
No mirror, no RouteGuard. The permission rides in the JWT (claims.permissions) and the server enforces it; a missing permission yields a 403 the UI surfaces. Nothing to add client-side beyond consuming the gated endpoint.
{X}Permissions and its All collection.RequirePermission(...); no duplicate IRequiredPermissionMetadatalib/permissions.ts (+ PERMISSION_CATALOG if role-editor-visible)<RouteGuard perms={[…]}>; permission added to ADMIN_PERMS test seedtest:e2e green