Jest fixes 58

This commit is contained in:
karilint
2026-03-20 13:57:09 +02:00
parent c10bed39c6
commit ce05873119
4 changed files with 209 additions and 121 deletions
+15
View File
@@ -72,6 +72,20 @@ When changing backend code or backend API tests, Codex must proactively apply th
- Be careful with generated ids in MariaDB-backed tests. Prefer deterministic id handling that works with the actual driver behavior instead of assuming `RETURNING` semantics.
- When debugging backend test failures, capture the real exception if possible before applying another speculative fix.
## Cypress / E2E Test Policy
When changing Cypress support code or E2E specs, Codex must proactively apply these rules:
- Prefer fixing the shared Cypress harness first when multiple specs fail in similar ways. Check `cypress/support/commands.js`, `cypress.config.js`, shared fixtures, and common login/reset helpers before patching many individual specs.
- Use deterministic database setup. Prefer a single shared `cy.resetDatabase()` helper over raw reset requests scattered through specs, and use `beforeEach` isolation when tests mutate shared seeded state.
- Treat Cypress support files as strict code too: custom commands, TS declaration files, fixtures used as typed metadata, and support helpers must satisfy ESLint, Prettier, and TypeScript rules.
- Be careful with Material UI wrappers and hidden inputs. Target the actual interactive element (for example the nested `input` inside a `TextField` wrapper) rather than wrapper divs or hidden native select inputs.
- Do not rely on live external services in E2E tests. Stub third-party requests such as Geonames lookups with `cy.intercept(...)` when the behavior under test is not the external service itself.
- Prefer stable post-action assertions over brittle transient UI text. If a save/create flow is asynchronous, verify the actual network response or returned id/slug and then navigate/assert from the durable saved state instead of depending only on an immediate toast or header text.
- Avoid over-asserting read-mode text for fields whose display formatting can legitimately differ from edit-mode values. For selectors such as time-unit sequences, assert against the stable UI state that the current component actually exposes.
- When a Cypress failure shows an application-side unhandled rejection, check whether the real fix belongs in production code, especially around RTK Query `unwrap()` calls, delete flows, or mutation side effects leaking rejected promises.
- Keep hard-coded seeded ids in E2E specs aligned with the current checked-in seed data. If API and E2E tests both exercise the same entity, prefer the same known-good ids.
## Validation Expectations
Before finishing, Codex should run the narrowest useful checks for the task, and run broader checks when the scope justifies it.
@@ -83,6 +97,7 @@ Typical commands from the repository root:
- `npm run test`
- `npm run lint:frontend`
- `npm run tsc:frontend`
- `npm run lint:cypress`
Backend-focused checks are also expected when relevant:
+2 -1
View File
@@ -136,7 +136,8 @@ describe('Creating a time unit', () => {
cy.wait('@saveTimeUnit').then(({ response }) => {
expect(response?.statusCode).to.eq(200)
const createdSlug = response?.body?.tu_name
expect(createdSlug, 'created time unit slug').to.be.a('string').and.not.be.empty
expect(createdSlug, 'created time unit slug').to.be.a('string')
expect(createdSlug, 'created time unit slug').to.not.equal('')
cy.visit(`/time-unit/${createdSlug}`)
})
cy.contains('C2N-o')
+42 -11
View File
@@ -31,6 +31,7 @@ This section summarizes the overall architecture, structure, and technologies of
- Environment / config: `.env` files, environment variables, and `.template.env` for defaults
### Frontend structure conventions
- **Pages (`frontend/src/pages/`)** are thin route-level shells. Keep them minimal: set metadata (e.g., `document.title`), read router params, and render feature components.
- **Feature components (`frontend/src/components/<Feature>/`)** hold the reusable UI and logic for a domain area (e.g., project create/edit flows, forms, selectors).
- Co-locate supporting pieces (e.g., `CoordinatorSelect`, `MembersMultiSelect`, `ProjectForm`) under the feature folder and import them into pages as needed.
@@ -79,7 +80,7 @@ This section summarizes the overall architecture, structure, and technologies of
## Authentication & Authorization
- Role-based access model (e.g., *coordinator* = admin-level users)
- Role-based access model (e.g., _coordinator_ = admin-level users)
- Backend middleware enforces authorization rules
- Frontend hides or disables restricted functionality for unauthorized users
@@ -93,6 +94,7 @@ This section summarizes the overall architecture, structure, and technologies of
- All new features require code review, test coverage, and documentation updates
### Linting and Type-Checking Expectations
- Run `npm run lint` and `npm run tsc` from the repo root before requesting review; use `npm run lint:backend` / `npm run lint:frontend` and `npm run tsc:backend` / `npm run tsc:frontend` when iterating in a single scope.
- If Cypress specs are touched, run `npm run lint:cypress` to satisfy the dedicated ESLint rules in `eslint.config.mjs`.
- Keep Prisma clients generated (`npm run prisma`) so TypeScript sees the emitted types referenced by backend utilities.
@@ -100,6 +102,7 @@ This section summarizes the overall architecture, structure, and technologies of
- Large fixtures or JSON assets (e.g., geodata) should remain excluded via `tsconfig.json`/`eslint.config.mjs` ignores; avoid importing them in type-checked code paths.
### Frontend Unit-Test Planning Guardrails
- When a feature touches shared frontend infrastructure such as `DetailView`, `EditableTable`, `DetailTabTable`, `UnsavedChangesProvider`, Redux API slices, or route-aware components, include explicit test-harness tasks in the plan.
- For each affected test, identify the required runtime context up front: `Provider` for Redux hooks, `MemoryRouter` or `createMemoryRouter` for router hooks, `PageContext` for detail/table helpers, and notification/dialog providers where hooks read context directly.
- Treat Vite-specific config (`import.meta.env`), ESM-only dependencies, and asset imports as Jest compatibility risks. Prefer narrow test-only shims or stable local abstractions over broad global remaps.
@@ -110,6 +113,7 @@ This section summarizes the overall architecture, structure, and technologies of
- When test doubles include click handlers or mock browser APIs, plan for lint-safe implementations too: avoid promise-returning event handlers when a void callback is expected, and keep Jest/browser polyfills compatible with strict ESLint rules (`require-await`, unsafe returns, unnecessary assertions, Prettier).
### Backend API-Test Planning Guardrails
- When backend work touches write flows, audit logging, reference joins, or test fixtures, include an explicit API-test impact review in the plan before changing code. Decide whether the failure is more likely caused by:
- a real backend regression,
- stale seeded test data,
@@ -128,34 +132,59 @@ This section summarizes the overall architecture, structure, and technologies of
- or assumptions that a second idempotent write replays the same unrelated changes.
- If local reproduction is difficult, plan one narrow debug path to capture the real backend exception before applying speculative fixes.
### Cypress / E2E Planning Guardrails
- When Cypress failures cluster across many specs, plan a shared-harness review first:
- `cypress/support/commands.js`
- `cypress.config.js`
- shared fixtures
- login/reset helpers
- Add an explicit decision about test isolation:
- whether the spec should use `before` or `beforeEach`
- whether shared seeded state is being mutated
- whether a common `cy.resetDatabase()` helper should replace ad hoc reset requests
- For MUI-heavy forms, plan selectors against the real interactive element, not wrapper nodes or hidden native inputs.
- If the spec depends on external lookups, plan to stub them with `cy.intercept(...)` instead of calling the real service.
- When create/update flows are involved, add a verification task for the actual network response and returned id/slug. Prefer asserting on durable saved state over immediate toast text or optimistic header content.
- If an E2E failure shows an uncaught application rejection, plan one production-code investigation path as well as a spec path. Do not assume every Cypress failure is test-only.
- For seeded entity ids used in Cypress routes, plan a quick seed verification step so the spec does not depend on stale composite keys or deleted fixtures.
- Add a task to review whether an assertion is checking the stable UI contract:
- edit-mode value vs read-mode text
- display label vs stored code
- delayed navigation vs immediate route assertion
---
# Codex Prompt for NOW Database Full-Stack Development
## 🧩 Feature Request
<<<
[Describe what you want the feature to do; user stories; data model needs;
permissions/roles; edge cases; performance concerns; accessibility considerations;
responsive UI expectations; icon usage (e.g. Material UI icons) if any; SEO; i18n;
any external APIs; acceptance examples]
>>>
> > >
---
## 📦 Deliverables (in this exact order)
### 1️⃣ Assumptions & Scope
- The NOW Database application is a full-stack TypeScript project with a **Node.js** backend and a **React** frontend.
- The backend uses **Express.js** (REST API) with a **MySQL/MariaDB** database managed via **Prisma ORM**.
- The frontend is built with **Vite + React**, using **Material UI (MUI)** for styling and **Redux Toolkit** for state management.
- Features often affect both backend and frontend components. Identify which parts of the system will be impacted (API endpoints, database schema, UI components, etc.).
- Authentication and authorization rely on role-based access (e.g., *coordinator* for admin-level users). Ensure all new endpoints respect these rules.
- Authentication and authorization rely on role-based access (e.g., _coordinator_ for admin-level users). Ensure all new endpoints respect these rules.
- Database changes should be additive (no breaking changes). Use environment variables for credentials or API keys.
- Follow existing conventions for routing, state management, folder structure, and error handling across backend and frontend.
---
### 2️⃣ High-Level Plan
Implementation should cover all layers of the stack:
- **Database & Models:** Update or add Prisma schema models if new data structures are needed. Create migrations via Prisma to reflect schema changes.
@@ -166,11 +195,13 @@ Implementation should cover all layers of the stack:
- **Testing:** Use **Jest** + **@testing-library/react** for frontend tests, and **Mocha** + **supertest** for backend API tests. Add end-to-end coverage with **Cypress** if needed.
Frontend test planning must call out required providers, router mode, module mocks, and Jest/Vite compatibility for each changed area.
Backend test planning must call out fixture source, generated-id expectations, external-service mocking, and whether update/audit assertions are intentionally narrow or broad.
Cypress planning must call out shared support/helper impact, DB reset strategy, external stubs/intercepts, and whether post-save assertions depend on durable saved state or transient UI.
- **Documentation & Deployment:** Update `.env` templates, Docker Compose, and internal documentation if configuration changes are needed.
---
### 3️⃣ Tasks (JSON)
Provide a **machine-readable JSON array** of granular tasks like:
```json
@@ -186,18 +217,12 @@ Provide a **machine-readable JSON array** of granular tasks like:
"prisma/schema.prisma"
],
"migrations": true,
"permissions": [
"Available to all logged-in users",
"Coordinator users can view extended metadata"
],
"permissions": ["Available to all logged-in users", "Coordinator users can view extended metadata"],
"acceptance_criteria": [
"Endpoint returns filtered results in under 1s for typical queries",
"Invalid filters return 400 errors with descriptive messages"
],
"test_plan": [
"Add unit tests for filter logic",
"Add integration tests for API route"
],
"test_plan": ["Add unit tests for filter logic", "Add integration tests for API route"],
"estimate_hours": 3.0,
"priority": "high"
}
@@ -207,6 +232,7 @@ Provide a **machine-readable JSON array** of granular tasks like:
---
### 4️⃣ Risks & Mitigations
- **Auth Failures:** Improperly applied role restrictions → verify middleware and test unauthorized access cases.
- **Data Migration:** Additive schema changes only. Backup before migrations.
- **Performance:** Use pagination and indexes for large tables.
@@ -216,10 +242,12 @@ Provide a **machine-readable JSON array** of granular tasks like:
- **Test Regressions:** Avoid broad Jest config changes that can alter unrelated suites. Prefer the smallest production fix or the narrowest test-specific shim that resolves the real incompatibility.
- **Lint Regressions:** Test helpers and polyfills must satisfy the same ESLint/Prettier rules as production code, especially hooks rules, async handler rules, and strict TypeScript linting in support files.
- **API Test Regressions:** Avoid backend API tests that depend on live third-party services, overly specific seed-state assumptions, or exact audit-row totals unless the task explicitly verifies those behaviors.
- **E2E Regressions:** Avoid Cypress specs that depend on immediate toast timing, stale seeded ids, live third-party lookups, or brittle wrapper-element selectors when a more durable network/state assertion is available.
---
### 5️⃣ Out of Scope
- Legacy data migration
- UI redesigns or theming beyond MUI conventions
- Full internationalization (i18n) system
@@ -228,6 +256,7 @@ Provide a **machine-readable JSON array** of granular tasks like:
---
### 6️⃣ Definition of Done ✅
- [ ] All acceptance criteria met
- [ ] Unit, integration, and E2E tests pass
- [ ] Code passes linting and type checks
@@ -236,6 +265,7 @@ Provide a **machine-readable JSON array** of granular tasks like:
- [ ] Frontend support code and test doubles pass hooks, async-handler, strict TypeScript, and Prettier lint rules
- [ ] Backend API tests use deterministic fixtures, generated-id assumptions are verified, and external integrations are mocked when appropriate
- [ ] Backend write flows strip helper-only metadata before shared persistence utilities
- [ ] Cypress changes use shared reset/login helpers, stable selectors, deterministic intercepts where needed, and durable post-save assertions
- [ ] Database migrations are applied and reversible
- [ ] Role-based permissions verified
- [ ] Documentation and changelog updated
@@ -244,4 +274,5 @@ Provide a **machine-readable JSON array** of granular tasks like:
---
## 🧠 Rules
- STOP after planning. Wait for explicit approval before coding.
@@ -6,6 +6,7 @@ Implement **exactly one task** from an approved feature plan.
---
## 🔧 Stack & Constraints
- **Language:** TypeScript (strict mode)
- **Backend:** Node.js 18 + Express.js (REST API)
- **ORM / DB:** Prisma • MySQL / MariaDB
@@ -53,11 +54,13 @@ Implement **exactly one task** from an approved feature plan.
✅ **Coding Requirements**
### Scope
- Implement **only** the task above. No bonus features.
- Respect `files_touched`, `migrations`, `settings_changes`, `permissions`.
- Place **route-level pages** in `frontend/src/pages/` as thin wrappers. Keep reusable UI/logic in `frontend/src/components/<Feature>/` (e.g., project create/edit flows live under `components/Project/` and pages import/render them).
### Local lint/type-check checklist (run before returning output)
- From repo root, run `npm run lint` and `npm run tsc`; for focused work use `npm run lint:backend` / `npm run lint:frontend` and `npm run tsc:backend` / `npm run tsc:frontend`.
- If Cypress specs changed, run `npm run lint:cypress` to cover `cypress/e2e/**/*.cy.js` rules from `eslint.config.mjs`.
- Ensure Prisma clients are generated (`npm run prisma`) so `tsc --noEmit` sees emitted types in backend utilities.
@@ -66,6 +69,7 @@ Implement **exactly one task** from an approved feature plan.
- For backend API-test work, prefer targeted validation while iterating, for example `cd backend && npm run test:api:local -- --runTestsByPath <file>`, and capture the real backend error before making another speculative fix.
### Frontend unit-test checklist (mandatory when frontend code or frontend tests change)
- Before changing Jest config, first check whether the failure is really caused by missing test runtime context. Components using Redux hooks must render inside `<Provider>`. Components using `useNavigate`, `useLocation`, `useBlocker`, or route loaders must render inside the correct router (`MemoryRouter` vs `createMemoryRouter` + `RouterProvider`).
- If a component depends on app-specific contexts such as page/detail/notification providers, supply those providers in the test harness or mock the smallest unrelated subtree that requires them.
- Prefer narrow fixes over global Jest hacks. Do not broadly remap application modules unless the task explicitly requires it. For `import.meta.env`, ESM-only libraries, assets, or browser APIs, use a dedicated Jest tsconfig, focused moduleNameMapper entries, or a small test stub.
@@ -78,6 +82,7 @@ Implement **exactly one task** from an approved feature plan.
- Treat test mocks and setup files as first-class code: remove unnecessary type assertions, satisfy `require-await` / `no-unsafe-return`, and keep mock/helper files Prettier-clean.
### Backend API-test checklist (mandatory when backend code or backend API tests change)
- Prefer deterministic backend tests. Do not leave API tests dependent on live external services, network access, or unstable third-party result counts. Mock outbound integrations such as Geonames at the test boundary.
- Use seeded ids and fixture values that are actually present in the checked-in test SQL. If a test depends on seed data, verify those ids/rows before changing expectations.
- When changing shared backend write helpers, assume the blast radius is large. Check how the change affects generated ids, update-log entry creation, reference join rows, and metadata stripping across locality/species/time-unit/time-bound flows.
@@ -88,13 +93,30 @@ Implement **exactly one task** from an approved feature plan.
- If a second write in a test is meant to be idempotent, make that follow-up payload narrow enough that it does not replay unrelated additions/removals and trigger a different server path.
- Backend test files themselves must satisfy ESLint, Prettier, and TypeScript rules; treat fetch mocks, response doubles, and debug helpers as production-quality code.
### Cypress / E2E checklist (mandatory when Cypress support files or E2E specs change)
- Check the shared Cypress harness first when failures cluster. Review `cypress/support/commands.js`, `cypress.config.js`, fixtures, and common login/reset helpers before patching many specs one by one.
- Prefer one deterministic `cy.resetDatabase()` helper over ad hoc reset requests in specs, and choose `beforeEach` isolation when tests mutate seeded data.
- Keep Cypress support code lint-clean too. Custom commands, support typings, helper fixtures, and E2E specs must satisfy ESLint, Prettier, and TypeScript rules, including Cypress-specific rules such as `unsafe-to-chain-command`.
- For MUI controls, target the real interactive element instead of wrapper divs or hidden native inputs. Do not use `clear()` or `click()` on non-clearable wrappers when the nested `input` is the real control.
- Stub third-party HTTP calls with `cy.intercept(...)` when the behavior under test is internal app behavior rather than the remote service.
- For create/update flows, prefer waiting on the real network request and using returned ids/slugs when that yields a more durable assertion than immediate route changes, toasts, or header text.
- Avoid brittle assertions on display text when the component exposes a more stable contract:
- use the current edit-field value instead of read-mode text when formatting differs,
- use stable buttons/URLs/table rows instead of transient notification timing.
- When a Cypress failure is caused by an uncaught application rejection, fix the production promise-handling path if appropriate instead of masking the error in the spec.
- Keep hard-coded seeded ids and routes aligned with the checked-in test seed data; reuse the same known-good ids across API and E2E tests when possible.
### Quality / Style
- Follow ESLint + Prettier + TypeScript strict.
- Typed interfaces / DTOs for all API payloads.
- Keep functions pure and modular; avoid duplicated logic.
### Architecture
**Backend**
- Organize code under `backend/src/{routes,controllers,services,middleware,utils}`.
- Use Express Router per module; validate requests with Zod / Yup if in scope.
- Access DB through Prisma client only.
@@ -102,6 +124,7 @@ Implement **exactly one task** from an approved feature plan.
- Return JSON responses with HTTP codes 200/400/403/404/500 as appropriate.
**Frontend**
- Use functional components with hooks.
- UI via MUI components + system styling (mobile-first / accessible).
- Follow atomic layout (`components/`, `pages/`, `redux/`, `hooks/`).
@@ -110,16 +133,19 @@ Implement **exactly one task** from an approved feature plan.
- Avoid introducing new runtime circular dependencies between Redux slices, API modules, and store setup. Prefer `import type` for type-only dependencies and keep API modules free of store/reducer runtime imports when possible.
**Database**
- Schema changes via Prisma migrations (`prisma migrate dev`).
- Only additive changes (no drops unless explicitly approved).
- Add indexes for heavy filter/sort fields.
**Auth & Security**
- Use middleware to verify JWT and roles.
- Sanitize all inputs; never trust client data.
- Protect restricted UI elements by role checks in React.
**Testing**
- Add tests only if `test_plan` indicates them.
- Aim for > 80 % coverage for changed modules.
- Backend tests mock Prisma or use test DB.
@@ -128,8 +154,10 @@ Implement **exactly one task** from an approved feature plan.
- Frontend test updates must verify the real render path is reachable before asserting controls; if a shared component stays in loading/error state, fix the harness rather than forcing the assertion.
- If Jest config is modified, confirm the change does not break unrelated suites by changing module resolution, transform mode, or global mocks.
- Lint and type-check test-support files too, including `src/tests/setup.ts` and `src/tests/mocks/*`; do not assume helper files are exempt from strict frontend rules.
- If Cypress files change, run the narrowest useful Cypress-related linting and prefer validating the touched spec/support files against the real request/response flow rather than only static DOM assumptions.
**Docs / CI**
- Update `.env.template`, README sections, and API docs when specified.
- CI must remain green (lint + type + test + build).
@@ -151,6 +179,7 @@ If a file is deleted, include `# FILE: <path> (deleted)` with no code block.
```markdown
# FILE: backend/src/routes/feedback.ts
import { Router } from "express";
import { createFeedback } from "../controllers/feedbackController";
@@ -159,17 +188,18 @@ router.post("/", createFeedback);
export default router;
# FILE: backend/src/controllers/feedbackController.ts
import { Request, Response } from "express";
import prisma from "../utils/prismaClient";
export const createFeedback = async (req: Request, res: Response) => {
try {
const { userId, subject, message } = req.body;
const feedback = await prisma.feedback.create({ data: { userId, subject, message } });
res.status(201).json(feedback);
} catch (err) {
res.status(500).json({ error: "Failed to create feedback" });
}
try {
const { userId, subject, message } = req.body;
const feedback = await prisma.feedback.create({ data: { userId, subject, message } });
res.status(201).json(feedback);
} catch (err) {
res.status(500).json({ error: "Failed to create feedback" });
}
};
```
@@ -177,24 +207,26 @@ For frontend tasks, example:
```markdown
# FILE: frontend/src/components/FeedbackForm.tsx
import { useState } from "react";
import { TextField, Button } from "@mui/material";
import axios from "axios";
export default function FeedbackForm() {
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async () => {
await axios.post("/api/feedback", { subject, message });
setSubject(""); setMessage("");
};
return (
<form onSubmit={e => { e.preventDefault(); handleSubmit(); }}>
<TextField label="Subject" value={subject} onChange={e => setSubject(e.target.value)} fullWidth />
<TextField label="Message" value={message} onChange={e => setMessage(e.target.value)} fullWidth multiline />
<Button type="submit" variant="contained">Send</Button>
</form>
);
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");
const handleSubmit = async () => {
await axios.post("/api/feedback", { subject, message });
setSubject(""); setMessage("");
};
return (
<form onSubmit={e => { e.preventDefault(); handleSubmit(); }}>
<TextField label="Subject" value={subject} onChange={e => setSubject(e.target.value)} fullWidth />
<TextField label="Message" value={message} onChange={e => setMessage(e.target.value)} fullWidth multiline />
<Button type="submit" variant="contained">Send</Button>
</form>
);
}
```
@@ -204,31 +236,37 @@ export default function FeedbackForm() {
```markdown
# FILE: backend/tests/feedback.test.ts
import request from "supertest";
import app from "../src/index";
describe("POST /api/feedback", () => {
it("creates feedback", async () => {
const res = await request(app).post("/api/feedback").send({ subject: "Hi", message: "Test" });
expect(res.status).toBe(201);
});
it("creates feedback", async () => {
const res = await request(app).post("/api/feedback").send({ subject: "Hi", message: "Test" });
expect(res.status).toBe(201);
});
});
```
---
🔒 **If Settings / Env / Permissions Are In Scope**
- Show the exact diff within `.env.template` or config file:
```markdown
# FILE: .env.template
DATABASE_URL="mysql://user:pass@db:3306/nowdatabase"
JWT_SECRET="replace_me"
```
- Role permissions → define middleware checks or Prisma seeds for roles.
---
🧯 **Non-Functional Requirements**
- Performance: Use pagination, indexes, and `select` fields to minimize payloads.
- Security: Never log secrets; use HTTPS; sanitize user input.
- Accessibility: Follow WCAG AA; test keyboard and screen reader flows.
@@ -237,6 +275,7 @@ JWT_SECRET="replace_me"
---
🚫 **Do Not**
- Include explanations or logs outside `# FILE:` blocks.
- Change unrelated files.
- Add new packages unless listed in `packages`.
@@ -247,6 +286,7 @@ JWT_SECRET="replace_me"
---
🧾 **Final Checklist Before Return**
- All acceptance criteria met.
- TypeScript compiles without errors.
- Backend & frontend tests pass.
@@ -254,6 +294,7 @@ JWT_SECRET="replace_me"
- Jest/browser shims are minimal and compatible with Vite config, ESM dependencies, RTK Query, and asset imports used by the changed code.
- Backend API tests use stable fixtures, deterministic generated-id handling, and mocked external integrations where applicable.
- Backend write paths do not leak helper metadata into shared DB utilities.
- Cypress support/spec changes use stable selectors, shared reset/login helpers, and deterministic intercepts where applicable.
- Production, test, and test-support files all satisfy hooks rules, async-handler rules, strict TypeScript linting, and Prettier.
- Prisma migrations exist if `migrations:true`.
- Role-based permissions verified.