# AGENTS.md — Polaris Task Force ## What this is Next.js 16 + Payload CMS 3.68.5 app for an Arma 3 unit. PostgreSQL database via `@payloadcms/db-postgres` + Drizzle. Tailwind CSS v4 (no config file — CSS-based). shadcn/ui (new-york style, `lucide` icons). Dark-themed frontend. ## Package manager **Bun** is the primary package manager (`bun.lock`, `bunfig.toml`). `pnpm-lock.yaml` also exists; use Bun for installs and running scripts. ## Essential commands ```bash bun install # install deps bun run dev # dev server (webpack, localhost:3000) bun run devsafe # clears .next cache then dev bun run build # production build (--max-old-space-size=8000, webpack) bun run lint # ESLint (next/core-web-vitals + next/typescript) bun run test # runs test:int then test:e2e sequentially bun run test:int # vitest integration tests only bun run test:e2e # playwright e2e tests only bun run db # drizzle-kit wrapper (e.g. bun run db migrate) bun run generate:types # regenerates payload-types.ts bun run generate:importmap # regenerates payload admin importMap ``` ## Type checking / linting order `bun run lint` runs ESLint. There is no separate `typecheck` script — TypeScript errors surface during `bun run lint` and `bun run build`. ## Test details - **Integration tests**: `tests/int/**/*.int.spec.ts` — Vitest with jsdom. Requires a live PostgreSQL database (connection from `.env`). Uses `dotenv/config` via `vitest.setup.ts`. - **E2E tests**: `tests/e2e/*.e2e.spec.ts` — Playwright (Chromium only). Auto-starts `bun run dev` via `webServer` config. Currently minimal (homepage smoke test). - Run a single integration test: `bun run vitest run tests/int/api.int.spec.ts` - Run a single e2e test: `bun run playwright test tests/e2e/frontend.e2e.spec.ts` ## Generated files — never edit manually - `src/payload-types.ts` — regenerated by `bun run generate:types` - `src/payload-generated-schema.ts` — regenerated by Payload db-schema generation - `src/app/(payload)/admin/importMap.js` — regenerated by `bun run generate:importmap` - `src/app/(payload)/layout.tsx` — auto-generated by Payload ## Path aliases - `@/*` → `./src/*` - `@payload-config` → `./src/payload.config.ts` ## App structure - `src/app/(frontend)/` — public-facing pages (dashboard, logistics, home). Layout has sidebar + auth check. - `src/app/(payload)/` — Payload admin panel and API routes. Auto-generated layout. - `src/app/my-route/` — example custom API route. ## Collections (Payload CMS) Organized by domain under `src/collections/`: - **users/** — Users (auth, username login), Ranks, Profiles, Awards, Qualifications, Assignments, Experience - **intelligence/** — Missions, Campaigns, Factions, Technologies - **logistics/** — Assets, Resources, Vehicles, Structures - **world/** — Maps, NarrativeEvents - **server/** — MissionFiles, ModLists - **game/** — GameRules (global), GameStructures, GameHardResources, GameEventLogs Access control helpers live in `src/utils/access-control/` (isRole, hasRoles). Roles: `guest`, `user`, `admin`, `developer`. ## Event Log System `src/collections/game/GameEventLogs.ts` — append-only `game-event-logs` collection. - **Schema**: `timestamp`, `type` (text/indexed), `message` (human-readable), `actor` (→ users), `structure` (→ game-structures), `targetCollection`/`targetId` (polymorphic ref), `data` (JSON blob). - **Emit**: `emitGameEvent(payload, { type, message, actor?, structure?, targetCollection?, targetId?, data? })` in `src/utils/event-log/emit.ts`. - **Types**: `EventTypes` constants in `src/utils/event-log/eventTypes.ts` — add new event categories there without touching the collection. - **Wiring**: Server actions in `actions.ts` emit events after successful mutations. `GameStructures` afterChange hook catches admin-panel storage edits. `Structures` beforeChange hook emits `structure:resize`. - **UI**: `EventLedger` component (`src/components/frontend/storage/EventLedger.tsx`) queries the REST API for a given `structureId` and renders a scrollable feed. Integrated in `StructureStorageView`. ## Auth Username-based login (no email login). Users log in via Payload admin with `username` only. ## Database PostgreSQL via `@payloadcms/db-postgres`. Schema defined in `src/payload-generated-schema.ts`. Migrations in `src/migrations/` (timestamp-named .ts + .json pairs, registered in `migrations/index.ts`). Drizzle config reads `DATABASE_URI` from env. In development, `postgresAdapter` uses `push: true` to auto-sync schema. ## Code style - **Prettier**: double quotes, trailing commas (all), 100 char print width, semicolons. - **ESLint**: `next/core-web-vitals` + `next/typescript`. `@typescript-eslint/no-unused-vars` warns (prefix unused with `_`). - **Tailwind v4**: no `tailwind.config` — configured via `@tailwindcss/postcss` in `postcss.config.mjs` and CSS imports. Use `cn()` from `@/lib/utils` for class merging. ## UI components shadcn/ui components live in `src/components/ui/`. Use `bunx shadcn@latest add ` to add new ones. Frontend components in `src/components/frontend/`. Payload admin custom components referenced in `payload.config.ts` under `admin.components`. ## Environment - `.env` — local dev (PostgreSQL connection string + PAYLOAD_SECRET) - `.env.example` — template (still shows MongoDB URI — outdated, the app uses PostgreSQL) - `.env.test` — test/staging database - `test.env` — NODE_OPTIONS for playwright (loaded by playwright config) ## Deploy `bun run deploy` bumps patch version (via `bun pm version patch`), builds, then runs `build/deploy.sh`. The `build/` directory is gitignored so the deploy script is not in the repo. ## Gotchas - `.env.example` shows MongoDB URI but the app uses PostgreSQL — trust `DATABASE_URI` format in `.env.test` as the real reference. - `bun run build` passes `--max-old-space-size=8000` — the build is memory-intensive. - The `devturbo` script uses Turbopack; `dev` and `devsafe` use webpack. These are different bundlers with different behavior. - Playwright tests auto-start the dev server — make sure port 3000 is free before running e2e. - Payload admin layout and importMap are auto-generated — do not edit by hand. - `.npmrc` sets `legacy-peer-deps=true` for dependency resolution compatibility.