Update root and per-directory AGENTS.md files to reflect the current state of the codebase: collection inventory, RBAC registry, base management, personnel pages, training minigames, Discord bot transfer and evaluation-reminder flows, banking currency config, migration wrapper, and Playwright/Vivaldi e2e details.
2.9 KiB
AGENTS.md — Utilities & Access Control
Parent:
../../AGENTS.md— RBAC overview, permission groups, event system.
Overview
10 files across 3 subdirectories. Core cross-cutting concerns: three-layer RBAC, fire-and-forget event logging, XP resolution.
Structure
utils/
access-control/ 8 files Permission checking, role gates, qualification queries
event-log/ 3 files Event emitter, type constants, formatting
xp/ 1 file Level resolver
Access control layers
Three independent access mechanisms, each used in different contexts:
1. hasPermission(payload, user, "domain:action")
Full RBAC check against src/permissions/index.ts (100+ permissions). Superuser bypass. Cached 30s via loadUserPermissions. Used in server actions and collection access functions.
2. isRole(role) / hasRoles(roles[])
Lightweight role checks. Used for quick conditional rendering (e.g., isRole("admin") for admin-only UI). No Payload call — reads from the user object directly.
3. hasLogisticsQualification() / hasIntelligenceQualification()
Queries Profiles.progression.qualifications for specific qualification strings (case-insensitive). Admin/developer always pass. Used to gate logistics-only and intelligence-only UI sections.
Event log system
Emitter: emitGameEvent(payload, { type, message, ... })
Fire-and-forget create on game-event-logs. Always sets system: true. Silent error catch (no throw). Always called AFTER successful mutation in server actions.
Event types: EventTypes constants (~105 types across 15+ categories)
Defined in eventTypes.ts. Use these constants for TypeScript narrowing on the type field. Categories include: mission:*, finance:*, market:*, structure:*, staff:*, logistics:*, bank:*, notification:*, locker:*, xp:*, ticket:*, wiki:*, minigame:*, attendance:*, system:*.
Display: formatType(type) — human-readable label for event types.
Where to look
| Task | Path |
|---|---|
| Add new RBAC permission | src/permissions/index.ts + use in hasPermission calls |
| Add qualification gate | access-control/hasLogisticsQualification.ts (or create similar) |
| Add event type constant | event-log/eventTypes.ts (add to EventTypes object) |
| Emit event from action | import { emitGameEvent } from "@/utils/event-log/emit" |
| Check qualification in layout | Use hasLogisticsQualification(payload, user) |
| Modify XP calculation | xp/resolveLevel.ts |
Anti-patterns
- NEVER throw in
emitGameEvent— it's fire-and-forget by design - NEVER use
isRolefor permission-sensitive operations — usehasPermissioninstead - NEVER hardcode qualification strings — use the constants in the qualification collection
- NEVER add event types without adding to
EventTypesconstants (breaks TypeScript narrowing)