From 1bf7c945cf084a2923eb2c5371dbca8bf7998b62 Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Sun, 2 Aug 2026 03:10:57 -0400 Subject: [PATCH] chore: update AGENTS.md with information about shipments, game-tick SSE notifications, storage rules enforcement --- AGENTS.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8c49b3f..6d91379 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## 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. +Next.js 16 + Payload CMS 3.79.1 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 @@ -15,7 +15,7 @@ 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 lint # BROKEN under Next 16 — see typecheck below 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 @@ -24,9 +24,15 @@ bun run generate:types # regenerates payload-types.ts bun run generate:importmap # regenerates payload admin importMap ``` -## Type checking / linting order +## Type checking / linting -`bun run lint` runs ESLint. There is no separate `typecheck` script — TypeScript errors surface during `bun run lint` and `bun run build`. +`bun run lint` is **broken** under Next 16 — `next lint` was removed and errors out with `Invalid project directory provided, no such directory: .../lint`. Don't rely on it. The reliable typecheck is: + +```bash +npx tsc --noEmit 2>&1 | grep -E "error TS" | grep -v "\.next/" +``` + +Known pre-existing errors, don't chase them: `src/utils/access-control/hasLogisticsQualification.ts` (lines 12, 19 — `'user' is possibly 'null'`) and `src/payload-generated-schema.ts:1848` (generated file). Anything else is yours. ## Test details @@ -49,7 +55,8 @@ bun run generate:importmap # regenerates payload admin importMap ## App structure -- `src/app/(frontend)/` — public-facing pages (dashboard, logistics, home). Layout has sidebar + auth check. +- `src/app/(frontend)/` — public-facing pages (dashboard, logistics, home). Layout has sidebar + auth check (guests get `LandingPage`, no shell). +- `src/app/login/` — standalone login route, intentionally OUTSIDE the gated `(frontend)` group. Own dark `` layout that reuses `(frontend)/styles.css`. - `src/app/(payload)/` — Payload admin panel and API routes. Auto-generated layout. - `src/app/my-route/` — example custom API route. @@ -59,10 +66,10 @@ 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 +- **logistics/** — Assets, Resources, Vehicles, Structures, Shipments - **world/** — Maps, NarrativeEvents - **server/** — MissionFiles, ModLists -- **game/** — GameRules (global), GameStructures, GameHardResources, GameEventLogs +- **game/** — GameRules (global), GameStructures, GameHardResources, GameEventLogs, GameVehicles Access control helpers live in `src/utils/access-control/` (isRole, hasRoles). Roles: `guest`, `user`, `admin`, `developer`. @@ -75,7 +82,7 @@ Access control helpers live in `src/utils/access-control/` (isRole, hasRoles). R - **Emit**: `emitGameEvent(payload, { type, message, actor?, structure?, targetCollection?, targetId?, data? })` in `src/utils/event-log/emit.ts`. Silently catches errors (fire-and-forget). Always sets `system: true`. - **Types**: `EventTypes` constants in `src/utils/event-log/eventTypes.ts` — add new event categories there without touching the collection. Using these constants gives TypeScript narrowing on the `type` field. - **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`. -- **targetCollection options**: `game-structures`, `structures`, `resources`, `assets`, `vehicles`, `factions`, `missions`, `campaigns`, `users`, `technologies`, `maps`. If a new collection is added that should be a valid target, add an option here. +- **targetCollection options**: `game-structures`, `structures`, `resources`, `assets`, `vehicles`, `game-vehicles`, `factions`, `missions`, `campaigns`, `users`, `technologies`, `maps`, `shipments`. If a new collection is added that should be a valid target, add an option here. - **Narrative events**: admins/developers can create entries manually in the Payload admin panel with `system: false` for GM-written narrative events. These are visually distinct in the UI (amber accent, book icon). ### UI @@ -98,6 +105,31 @@ Access control helpers live in `src/utils/access-control/` (isRole, hasRoles). R ### Non-structure events The `EventLedger` currently filters by `structure.id`. For event logs scoped to other entities (e.g., faction finance ledger), a new ledger variant would need to be created that queries by `targetCollection` + `targetId` instead. +## Shipments & game tick + +Shipping simulation (`src/collections/logistics/Shipments.ts`, `src/scripts/`, `src/lib/shipping.ts`, `src/lib/distance.ts`): + +- **Shipment fields**: `origin`/`destination` → `game-structures`, `transportVehicle` → `game-vehicles`, `cargo[]` (relationship to resources/assets/vehicles + amount), `distance`, `fuelCost`/`fuelConsumed`, `status` (`pending`/`dispatched`/`in_transit`/`arrived`/`completed`/`cancelled`/`failed`/`stranded`), `autoReturn` checkbox, `failureReason`. +- **Game tick**: `bun run payload game-tick` — a `bin` registered on `payload.config.ts`, NOT an npm script. It processes active shipments + fuel consumption, then `process.exit(0)`. +- **Arrival handling** (`src/scripts/processShipmentTick.ts`): destination storage rules are re-checked on arrival; rejected cargo bounces back to origin, shipment goes `failed`, `ShipmentFail` event logged. +- **GameRules tuning**: `proximityThreshold` and `gameTickIntervalMinutes` live on the global `game-rules` doc. +- **UI**: `src/app/(frontend)/logistics/shipments/` (list + `[id]` detail with `ShipmentActions` controls), `src/app/(frontend)/logistics/game-vehicles/` (deployed vehicle views). + +### Realtime SSE pipeline (in-memory, single-instance only) + +gameTick → POST `/api/game-tick/notify` (guarded by `x-game-tick-secret` header = `GAME_TICK_NOTIFY_SECRET` env) → in-process bus (`src/lib/realtime/bus.ts`, module-level subscriber Set) → SSE `GET /api/realtime` → `GameTickRealtime` (`src/components/frontend/realtime/`) calls `router.refresh()` and dispatches `ptf:game-tick` → consumers via `src/hooks/useGameTick.ts` (`ShipmentToasts`, `EventLedger`). **Will not work across multiple server instances.** + +- `GameTickRealtime` mounts only for logged-in users; `ShipmentToasts` only for logistics-qualified users. +- `hasLogisticsQualification(payload, user)` (`src/utils/access-control/hasLogisticsQualification.ts`) queries Profiles `progression.qualifications` for "logistics" (case-insensitive); admin/developer always pass. Used to gate logistics-only UI. + +## Storage rules (logistics) + +`Structure` collection has `allowedStorage` (per-item caps), `prohibitedStorage`, and `restrictToAllowed` (whitelist gate) under `storage`. Shared logic in `src/lib/storageRules.ts` (`checkStorageDeposit`, `isResourceProhibited`, `isResourceWhitelisted`, `getResourceStorageCap`, `storageViolationMessage`). + +- Enforcement order: **prohibited → whitelist → per-item cap**, counting grid + void storage. +- Enforced in: `structures/actions.ts` (`addResource`, `transferResource`, `placeResourceOnGrid`), `shipments/actions.ts` (`createShipment` destination check), and `processShipmentTick.ts` on arrival. +- UI: `ManageStorageDialog` caps deposits to `min(mass, allowance)` and filters non-whitelisted items; structure page shows an amber "whitelist mode" banner when `restrictToAllowed` is set. + ## Narrative Events System `src/collections/world/NarrativeEvents.ts` — `narrative-events` collection. A flowchart-based narrative event editor using `@xyflow/react` (React Flow) in the Payload admin panel. @@ -119,6 +151,11 @@ The `EventLedger` currently filters by `structure.id`. For event logs scoped to Username-based login (no email login). Users log in via Payload admin with `username` only. +- `src/app/(frontend)/layout.tsx` is the gate: guests render `LandingPage` (no app shell); authed users get sidebar + `GameTickRealtime`. +- `/login` (`src/app/login/page.tsx` + `src/components/frontend/auth/LoginForm.tsx`) POSTs `{ username, password }` to `/api/users/login`, then `router.push("/")` + `router.refresh()`. The page redirects already-authed users to `/`. +- Logout lives in `NavUser` (sidebar) → `/api/users/logout`. +- Tip: a corrupted `payload-token` cookie causes an infinite login loop (`Unexpected end of JSON input` on `/api/users/me`) — clear cookies/use incognito. Dev user `dev` / `Test123`. + ## 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. @@ -140,7 +177,7 @@ shadcn/ui components live in `src/components/ui/`. Use `bunx shadcn@latest add < ## Environment - `.env` — local dev (PostgreSQL connection string + PAYLOAD_SECRET) -- `.env.example` — template (still shows MongoDB URI — outdated, the app uses PostgreSQL) +- `.env.example` — template. `DATABASE_URI` line still shows MongoDB (outdated — trust `.env.test` for the real Postgres format), but `APP_URL` + `GAME_TICK_NOTIFY_SECRET` are current and required by the game tick. - `.env.test` — test/staging database - `test.env` — NODE_OPTIONS for playwright (loaded by playwright config)