prr-kit 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +260 -235
  3. package/docs/assets/banner.svg +33 -165
  4. package/docs/assets/how-it-works.svg +87 -0
  5. package/package.json +60 -60
  6. package/src/core/agents/prr-master.agent.yaml +18 -7
  7. package/src/core/tasks/clear.md +140 -0
  8. package/src/core/tasks/help.md +15 -13
  9. package/src/core/workflows/clear/workflow.md +6 -0
  10. package/src/core/workflows/help/workflow.md +6 -0
  11. package/src/core/workflows/party-mode/steps/step-01-load-reviewers.md +35 -24
  12. package/src/core/workflows/party-mode/steps/step-02-discussion.md +45 -25
  13. package/src/core/workflows/party-mode/workflow.md +2 -2
  14. package/src/prr/agents/architecture-reviewer.agent.yaml +65 -45
  15. package/src/prr/agents/business-reviewer.agent.yaml +66 -0
  16. package/src/prr/agents/general-reviewer.agent.yaml +64 -48
  17. package/src/prr/agents/performance-reviewer.agent.yaml +65 -45
  18. package/src/prr/agents/security-reviewer.agent.yaml +67 -43
  19. package/src/prr/config-template.yaml +97 -0
  20. package/src/prr/data/stacks/actix.md +55 -0
  21. package/src/prr/data/stacks/alpine.md +47 -0
  22. package/src/prr/data/stacks/android.md +53 -0
  23. package/src/prr/data/stacks/angular.md +96 -0
  24. package/src/prr/data/stacks/ansible.md +55 -0
  25. package/src/prr/data/stacks/apollo.md +54 -0
  26. package/src/prr/data/stacks/astro.md +48 -0
  27. package/src/prr/data/stacks/aws-cdk.md +55 -0
  28. package/src/prr/data/stacks/axum.md +56 -0
  29. package/src/prr/data/stacks/babylonjs.md +55 -0
  30. package/src/prr/data/stacks/bash.md +53 -0
  31. package/src/prr/data/stacks/bevy.md +53 -0
  32. package/src/prr/data/stacks/bootstrap.md +52 -0
  33. package/src/prr/data/stacks/bun.md +55 -0
  34. package/src/prr/data/stacks/cpp.md +57 -0
  35. package/src/prr/data/stacks/csharp.md +95 -0
  36. package/src/prr/data/stacks/css.md +55 -0
  37. package/src/prr/data/stacks/cypress.md +53 -0
  38. package/src/prr/data/stacks/d3.md +53 -0
  39. package/src/prr/data/stacks/deno.md +49 -0
  40. package/src/prr/data/stacks/django.md +92 -0
  41. package/src/prr/data/stacks/docker.md +79 -0
  42. package/src/prr/data/stacks/drizzle.md +54 -0
  43. package/src/prr/data/stacks/dynamodb.md +55 -0
  44. package/src/prr/data/stacks/electron.md +44 -0
  45. package/src/prr/data/stacks/elixir.md +53 -0
  46. package/src/prr/data/stacks/expo.md +53 -0
  47. package/src/prr/data/stacks/expressjs.md +82 -0
  48. package/src/prr/data/stacks/fastapi.md +88 -0
  49. package/src/prr/data/stacks/fastify.md +60 -0
  50. package/src/prr/data/stacks/fiber.md +55 -0
  51. package/src/prr/data/stacks/firebase.md +43 -0
  52. package/src/prr/data/stacks/flask.md +46 -0
  53. package/src/prr/data/stacks/flutter.md +75 -0
  54. package/src/prr/data/stacks/gin.md +57 -0
  55. package/src/prr/data/stacks/github-actions.md +71 -0
  56. package/src/prr/data/stacks/go.md +88 -0
  57. package/src/prr/data/stacks/godot.md +56 -0
  58. package/src/prr/data/stacks/graphql.md +76 -0
  59. package/src/prr/data/stacks/grpc.md +56 -0
  60. package/src/prr/data/stacks/haskell.md +48 -0
  61. package/src/prr/data/stacks/helm.md +54 -0
  62. package/src/prr/data/stacks/hono.md +54 -0
  63. package/src/prr/data/stacks/htmx.md +38 -0
  64. package/src/prr/data/stacks/java.md +87 -0
  65. package/src/prr/data/stacks/jest-vitest.md +87 -0
  66. package/src/prr/data/stacks/jquery.md +50 -0
  67. package/src/prr/data/stacks/junit.md +53 -0
  68. package/src/prr/data/stacks/kotlin.md +89 -0
  69. package/src/prr/data/stacks/kubernetes.md +148 -0
  70. package/src/prr/data/stacks/langchain.md +56 -0
  71. package/src/prr/data/stacks/laravel.md +56 -0
  72. package/src/prr/data/stacks/libgdx.md +46 -0
  73. package/src/prr/data/stacks/lit.md +49 -0
  74. package/src/prr/data/stacks/love2d.md +51 -0
  75. package/src/prr/data/stacks/lua.md +51 -0
  76. package/src/prr/data/stacks/mobx.md +54 -0
  77. package/src/prr/data/stacks/mongodb.md +85 -0
  78. package/src/prr/data/stacks/monogame.md +51 -0
  79. package/src/prr/data/stacks/mysql.md +57 -0
  80. package/src/prr/data/stacks/nestjs.md +95 -0
  81. package/src/prr/data/stacks/nextjs.md +88 -0
  82. package/src/prr/data/stacks/nginx.md +55 -0
  83. package/src/prr/data/stacks/node.md +56 -0
  84. package/src/prr/data/stacks/nuxtjs.md +91 -0
  85. package/src/prr/data/stacks/openai-api.md +54 -0
  86. package/src/prr/data/stacks/opengl.md +54 -0
  87. package/src/prr/data/stacks/phaser.md +54 -0
  88. package/src/prr/data/stacks/phoenix.md +55 -0
  89. package/src/prr/data/stacks/php.md +56 -0
  90. package/src/prr/data/stacks/playwright.md +86 -0
  91. package/src/prr/data/stacks/postgresql.md +60 -0
  92. package/src/prr/data/stacks/prisma.md +81 -0
  93. package/src/prr/data/stacks/pygame.md +52 -0
  94. package/src/prr/data/stacks/pytest.md +53 -0
  95. package/src/prr/data/stacks/python.md +94 -0
  96. package/src/prr/data/stacks/pytorch.md +54 -0
  97. package/src/prr/data/stacks/qwik.md +50 -0
  98. package/src/prr/data/stacks/rails.md +48 -0
  99. package/src/prr/data/stacks/react-native.md +77 -0
  100. package/src/prr/data/stacks/react.md +104 -0
  101. package/src/prr/data/stacks/redis.md +76 -0
  102. package/src/prr/data/stacks/redux.md +107 -0
  103. package/src/prr/data/stacks/remix.md +51 -0
  104. package/src/prr/data/stacks/rust.md +88 -0
  105. package/src/prr/data/stacks/sass.md +51 -0
  106. package/src/prr/data/stacks/scala.md +50 -0
  107. package/src/prr/data/stacks/scikit-learn.md +53 -0
  108. package/src/prr/data/stacks/sequelize.md +54 -0
  109. package/src/prr/data/stacks/socket-io.md +54 -0
  110. package/src/prr/data/stacks/solidity.md +53 -0
  111. package/src/prr/data/stacks/solidjs.md +45 -0
  112. package/src/prr/data/stacks/spring-boot.md +92 -0
  113. package/src/prr/data/stacks/sql.md +85 -0
  114. package/src/prr/data/stacks/sqlite.md +55 -0
  115. package/src/prr/data/stacks/styled-components.md +51 -0
  116. package/src/prr/data/stacks/supabase.md +57 -0
  117. package/src/prr/data/stacks/svelte.md +77 -0
  118. package/src/prr/data/stacks/sveltekit.md +54 -0
  119. package/src/prr/data/stacks/swift.md +61 -0
  120. package/src/prr/data/stacks/tailwindcss.md +10 -0
  121. package/src/prr/data/stacks/tanstack-query.md +48 -0
  122. package/src/prr/data/stacks/tauri.md +52 -0
  123. package/src/prr/data/stacks/terraform.md +53 -0
  124. package/src/prr/data/stacks/three.md +53 -0
  125. package/src/prr/data/stacks/trpc.md +49 -0
  126. package/src/prr/data/stacks/typeorm.md +40 -0
  127. package/src/prr/data/stacks/typescript.md +83 -0
  128. package/src/prr/data/stacks/unity.md +61 -0
  129. package/src/prr/data/stacks/unreal.md +58 -0
  130. package/src/prr/data/stacks/vite.md +48 -0
  131. package/src/prr/data/stacks/vue3.md +95 -0
  132. package/src/prr/data/stacks/vulkan.md +53 -0
  133. package/src/prr/data/stacks/wasm.md +49 -0
  134. package/src/prr/data/stacks/webpack.md +48 -0
  135. package/src/prr/data/stacks/zig.md +51 -0
  136. package/src/prr/data/stacks/zustand.md +56 -0
  137. package/src/prr/workflows/1-discover/select-pr/steps/step-05-confirm.md +1 -0
  138. package/src/prr/workflows/1-discover/select-pr/workflow.md +1 -1
  139. package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-01-analyze-files.md +334 -0
  140. package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-02-collect-sources.md +451 -0
  141. package/src/prr/workflows/2-analyze/collect-pr-context/steps/step-03-build-knowledge-base.md +337 -0
  142. package/src/prr/workflows/2-analyze/collect-pr-context/workflow.md +123 -0
  143. package/src/prr/workflows/2-analyze/describe-pr/steps/step-02-classify.md +12 -6
  144. package/src/prr/workflows/2-analyze/describe-pr/steps/step-03-walkthrough.md +59 -1
  145. package/src/prr/workflows/3-review/architecture-review/checklist.md +4 -0
  146. package/src/prr/workflows/3-review/architecture-review/instructions.xml +32 -4
  147. package/src/prr/workflows/3-review/architecture-review/workflow.yaml +17 -18
  148. package/src/prr/workflows/3-review/business-review/checklist.md +27 -0
  149. package/src/prr/workflows/3-review/business-review/instructions.xml +153 -0
  150. package/src/prr/workflows/3-review/business-review/workflow.yaml +17 -0
  151. package/src/prr/workflows/3-review/general-review/checklist.md +5 -1
  152. package/src/prr/workflows/3-review/general-review/instructions.xml +39 -8
  153. package/src/prr/workflows/3-review/general-review/workflow.yaml +17 -18
  154. package/src/prr/workflows/3-review/performance-review/checklist.md +3 -1
  155. package/src/prr/workflows/3-review/performance-review/instructions.xml +10 -3
  156. package/src/prr/workflows/3-review/performance-review/workflow.yaml +17 -18
  157. package/src/prr/workflows/3-review/security-review/checklist.md +2 -1
  158. package/src/prr/workflows/3-review/security-review/instructions.xml +8 -3
  159. package/src/prr/workflows/3-review/security-review/workflow.yaml +18 -19
  160. package/src/prr/workflows/4-improve/improve-code/workflow.yaml +17 -18
  161. package/src/prr/workflows/6-report/generate-report/steps/step-01-collect.md +9 -2
  162. package/src/prr/workflows/6-report/generate-report/steps/step-02-organize.md +28 -7
  163. package/src/prr/workflows/6-report/generate-report/steps/step-03-write.md +6 -4
  164. package/src/prr/workflows/6-report/generate-report/templates/review-report.template.md +124 -78
  165. package/src/prr/workflows/6-report/post-comments/steps/step-01-format.md +104 -13
  166. package/src/prr/workflows/6-report/post-comments/steps/step-02-post.md +92 -21
  167. package/src/prr/workflows/6-report/post-comments/workflow.md +6 -0
  168. package/src/prr/workflows/quick/workflow.md +138 -32
  169. package/src/prr/workflows/0-setup/collect-project-context/steps/step-01-scan-configs.md +0 -106
  170. package/src/prr/workflows/0-setup/collect-project-context/steps/step-02-extract-rules.md +0 -131
  171. package/src/prr/workflows/0-setup/collect-project-context/steps/step-03-ask-context.md +0 -194
  172. package/src/prr/workflows/0-setup/collect-project-context/steps/step-04-save-context.md +0 -161
  173. package/src/prr/workflows/0-setup/collect-project-context/workflow.md +0 -58
@@ -0,0 +1,91 @@
1
+ # Nuxt.js — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: nuxt.config.ts, nuxt.config.js, .nuxt/, pages/, composables/, server/api/, useAsyncData, useFetch, defineNuxtPlugin, defineNuxtRouteMiddleware, useState, useRuntimeConfig
5
+
6
+ ---
7
+
8
+ ## Security
9
+
10
+ - **[CRITICAL]** `v-html` used with user-supplied content → XSS amplified by SSR cache poisoning: the rendered HTML is cached and served to all subsequent users. Remove `v-html` entirely; use a sanitisation library (DOMPurify) only as a last resort and never in SSR context.
11
+ - **[CRITICAL]** Server-side secrets accessed inside `useAsyncData` / `useFetch` handlers that run on both server and client → private keys, API secrets, or DB credentials are serialised into the JS bundle and exposed publicly. Move secret usage to `server/` directory routes or composables guarded with `import.meta.server`.
12
+ - **[CRITICAL]** Missing authentication / authorisation checks in `server/api/` route handlers → all endpoints are publicly reachable with no protection. Use `defineEventHandler` with an auth middleware (e.g. `server/middleware/auth.ts`) that validates session tokens before any business logic executes.
13
+ - **[HIGH]** Secrets placed in `runtimeConfig.public` → those values are inlined into the client bundle and visible to anyone who inspects the page source. Move confidential values to the non-public `runtimeConfig` block; access them only in `server/` code.
14
+ - **[HIGH]** No CSRF protection on state-changing `server/api/` routes (POST / PUT / DELETE / PATCH) → cross-site request forgery attacks succeed silently. Validate the `Origin` / `Referer` header or use a CSRF token library (e.g. `nuxt-csrf`) on all mutating endpoints.
15
+ - **[HIGH]** `useRequestHeaders()` forwarding the full incoming header map to an upstream service → Host-header injection; Authorization tokens intended for the Nuxt server are forwarded to a third-party backend. Explicitly allow-list only the headers the upstream legitimately needs.
16
+ - **[HIGH]** Nuxt DevTools left enabled in production builds → exposes the full component tree, Pinia state, all registered routes, and payload data to anyone who opens the browser panel. Set `devtools: { enabled: false }` in `nuxt.config.ts` and gate it behind a NODE_ENV check.
17
+ - **[HIGH]** `` or `useFetch` calls to internal `server/api/` routes from SSR context pass cookies automatically only when the base URL matches; a misconfigured `NUXT_PUBLIC_API_BASE` sends unauthenticated requests in production. Always set `baseURL` explicitly and verify cookie-forwarding behaviour per environment.
18
+ - **[MEDIUM]** `useState` keys not namespaced (e.g. `useState("data")`) → key collisions across modules or third-party packages cause one module's state to silently overwrite another's. Use unique, module-scoped keys: `useState("moduleName:data")`.
19
+ - **[MEDIUM]** `server/api/` auth endpoints (login, password reset) missing rate limiting → brute-force and credential-stuffing attacks succeed unchallenged. Add a rate-limiter middleware backed by `unstorage` or a Nitro plugin.
20
+ - **[MEDIUM]** Nitro preset mismatch between `nuxt.config.ts` and the actual deployment target (e.g. `node-server` preset deployed to Cloudflare Workers) → the app fails silently or uses the wrong runtime APIs. Set `nitro.preset` explicitly and validate against the hosting platform requirements.
21
+ - **[MEDIUM]** Open redirect in `navigateTo()` using an unvalidated user-supplied URL → phishing via a trusted domain. Validate that the target URL is relative or belongs to an allow-listed domain before calling `navigateTo()`.
22
+ - **[LOW]** Sensitive filenames placed in the `public/` directory (e.g. `public/backup.sql`) → served as static assets with no authentication. Audit `public/` regularly; never store non-public files there.
23
+ - **[LOW]** `console.log` statements in `server/` routes that log request bodies or auth tokens → credentials appear in server logs. Strip or redact all sensitive values before logging; use a structured logger with field filtering.
24
+
25
+ ---
26
+
27
+ ## Performance
28
+
29
+ - **[CRITICAL]** `useAsyncData` / `useFetch` called with `lazy: false` (the default) for slow upstream APIs → navigation is completely blocked until the API responds, causing multi-second white screens. Use `lazy: true` (or `useLazyFetch`) paired with a loading skeleton to unblock navigation immediately.
30
+ - **[HIGH]** Double-fetching: data fetched during SSR via `useFetch` then re-fetched again in `onMounted` → the payload already hydrated the Ref; the second fetch is wasteful and can cause flickering. Remove the `onMounted` fetch and trust Nuxt's payload hydration.
31
+ - **[HIGH]** Large `defineNuxtPlugin` bodies not marked `parallel: true` or not lazy-loaded → all non-parallel plugins execute serially before the app boots, bloating startup time. Mark independent plugins `{ parallel: true }`; defer heavy initialisation with dynamic `import()`.
32
+ - **[HIGH]** `payloadExtraction: false` not set for static sites with frequently changing data → CDN serves a stale pre-rendered payload, ignoring fresh API data until the next deployment. Set `payloadExtraction: false` or use ISR / SWR `routeRules` to control freshness per route.
33
+ - **[HIGH]** `useAsyncData` called without a `watch` option when the fetched URL depends on reactive route params → stale data is displayed when the user navigates between dynamic routes. Add `watch: [() => route.params.id]` to re-trigger the fetch on every param change.
34
+ - **[HIGH]** `useLazyAsyncData` / `useLazyFetch` not used for below-the-fold content → all async data blocks first paint even for content the user cannot yet see. Apply lazy variants with `pending` guards for all below-fold sections.
35
+ - **[MEDIUM]** Non-unique `useAsyncData` key reused across multiple components → Nuxt de-duplicates requests by key; one component silently overwrites another's cached result. Make every key unique, e.g. using a template literal that includes a dynamic ID.
36
+ - **[MEDIUM]** Client-only components (charts, maps, canvas, WebGL) rendered on the server without `<ClientOnly>` or `ssr: false` → SSR throws or produces hydration mismatches and flickering on mount. Wrap with `<ClientOnly>` or add `definePageMeta({ ssr: false })` on the containing page.
37
+ - **[MEDIUM]** `@nuxt/image` module not used for `<img>` tags → no automatic WebP conversion, resizing, lazy loading, or blur placeholders. Replace raw `<img>` with `<NuxtImg>` or `<NuxtPicture>`.
38
+ - **[MEDIUM]** Nuxt's automatic route-level code splitting bypassed by importing page components manually → the entire page graph ends up in the main bundle. Let Nuxt handle page imports via the `pages/` convention; avoid manual dynamic imports of page components.
39
+ - **[MEDIUM]** `` called directly in `setup()` at the top level instead of `useFetch` → no SSR execution, no payload deduplication, no caching; an extra network round-trip occurs on the client after hydration. Use `useFetch` / `useAsyncData` for all universal data fetching.
40
+ - **[MEDIUM]** No `routeRules` configured per route → every page uses the same rendering strategy regardless of caching needs. Use `routeRules` to apply ISR/SWR to mostly-static pages and SSR only where truly dynamic data is required.
41
+ - **[LOW]** Critical navigation links missing `<NuxtLink prefetch>` → the next route's JS chunk is not prefetched on hover or viewport visibility, causing a noticeable navigation delay. Add the `prefetch` prop to high-traffic internal links.
42
+ - **[LOW]** Page titles set as raw full strings per page instead of using a `titleTemplate` in `useHead` → inconsistent titles and duplicated boilerplate. Define a global `titleTemplate` in `app.vue` and set only the short title per page.
43
+
44
+ ---
45
+
46
+ ## Architecture
47
+
48
+ - **[HIGH]** Business logic (API calls, data transformation, validation) placed directly in `pages/` or component `<script setup>` blocks → code is not reusable or testable in isolation. Extract to `use*` composables in `composables/`; pages should be thin orchestration layers only.
49
+ - **[HIGH]** Cross-component state managed with ad hoc `useState` calls scattered across the codebase instead of Pinia stores → state synchronisation bugs, race conditions, and no DevTools support. Introduce Pinia via `@pinia/nuxt` and centralise shared state in typed stores.
50
+ - **[HIGH]** Direct database or ORM calls inside `pages/` or universal composables → no security boundary; DB logic executes on the client in SPA mode. All DB access must live in `server/api/` or `server/utils/`, which are never bundled for the client.
51
+ - **[HIGH]** `defineNuxtRouteMiddleware` not handling errors with `abortNavigation` or `navigateTo` → an unhandled throw inside middleware causes a blank white screen with no user feedback. Wrap logic in try/catch and call `navigateTo("/error")` on failure.
52
+ - **[MEDIUM]** `useNuxtApp()` called outside a Nuxt plugin, composable, or component `setup` context → returns `undefined` at runtime with a cryptic error. Pass the app instance via plugin provide / inject instead of calling `useNuxtApp()` in arbitrary scopes.
53
+ - **[MEDIUM]** Pinia store not using `#imports` auto-import → the store is instantiated before the Nuxt context is ready during SSR, causing hydration mismatches. Rely on Nuxt's auto-import for `defineStore`, `ref`, and `computed`.
54
+ - **[MEDIUM]** `definePageMeta` missing `layout` or `middleware` declarations on pages that require them → pages render with the wrong layout or skip required auth guards. Always declare `layout` and `middleware` explicitly via `definePageMeta`.
55
+ - **[MEDIUM]** `server/middleware/` execution order not considered → a business-logic middleware can run before the auth middleware that sets `event.context.user`. Name files with numeric prefixes (`01.auth.ts`, `02.tenant.ts`) to enforce deterministic order.
56
+ - **[MEDIUM]** Heavy features (auth, i18n, analytics) configured manually without official Nuxt modules → SSR edge cases (cookie forwarding, locale detection, hydration) are handled incorrectly. Use `nuxt-auth`, `@nuxtjs/i18n`, etc., which are built for Nuxt's SSR lifecycle.
57
+ - **[MEDIUM]** Server routes placed in `api/` at the project root instead of `server/api/` → Nuxt 3 only scans `server/api/`; routes are silently ignored and return 404. Move all server routes under `server/api/`.
58
+ - **[LOW]** `routeRules` not used to declare per-route rendering strategy → ISR and SWR candidates default to full SSR on every request, wasting server resources. Map static and near-static routes to `{ isr: 60 }` or `{ swr: true }`.
59
+ - **[LOW]** `app.vue` contains substantive business logic instead of only `<NuxtLayout>` / `<NuxtPage>` → makes the root component a maintenance burden. Delegate all logic to layouts, pages, and composables.
60
+
61
+ ---
62
+
63
+ ## Code Quality
64
+
65
+ - **[HIGH]** `useAsyncData` handler function does not explicitly `return` the fetched value → the `data` Ref is always `undefined` with no error thrown, making this bug silent and hard to diagnose. The handler must return the value: `useAsyncData("key", () => ("/api/items"))`.
66
+ - **[HIGH]** `navigateTo()` called inside `setup()` without `return` → navigation fires but the component continues executing setup logic, potentially causing errors or double rendering. Always `return navigateTo()` from `setup()` or from middleware.
67
+ - **[HIGH]** `useAsyncData` / `useFetch` not typed with a generic (`useAsyncData<MyType>`) → `data` is typed as `Ref<unknown>`, defeating TypeScript's value. Add explicit generic types and define response interfaces in a shared `types/` directory.
68
+ - **[HIGH]** `useAsyncData` key is a dynamic expression evaluated once at call time that never changes on re-render → the cached result is never invalidated when inputs change. Use a computed key string or add a `watch` option for reactive dependencies.
69
+ - **[MEDIUM]** Options API and Composition API mixed within the same component → confusing code structure, incompatible lifecycle hooks, and subtle reactivity bugs. Standardise on Composition API with `<script setup>` across the entire codebase.
70
+ - **[MEDIUM]** Manual `watch` placed on `useFetch` result data to trigger a second fetch → causes extra network requests and timing races. Use the built-in `watch` option of `useFetch` / `useAsyncData` instead.
71
+ - **[MEDIUM]** Options API components not using `defineNuxtComponent` → the component loses Nuxt's SSR async data support (`asyncData` / `fetch` hooks). Replace `defineComponent` with `defineNuxtComponent` for all Options API components.
72
+ - **[MEDIUM]** `useFetch` used for client-only event-driven requests and `` used for universal data → inconsistent patterns confuse team members. Convention: `useFetch` / `useAsyncData` for universal/SSR data; `` for event-driven client-only calls.
73
+ - **[MEDIUM]** Error states from `useFetch` / `useAsyncData` not rendered in the template → users see a blank section with no feedback when an API call fails. Always destructure `{ data, pending, error }` and display a corresponding error UI.
74
+ - **[LOW]** Legacy plugin inject pattern (`context. = ...`) used instead of Nuxt 3 provide / inject → not typed, not tree-shakeable, and deprecated. Replace with `nuxtApp.provide("myService", ...)` in the plugin and `useNuxtApp().` in composables.
75
+ - **[LOW]** `definePageMeta` called with non-literal values (variables, computed expressions) → Nuxt's static analyser cannot extract metadata at build time and silently ignores it. `definePageMeta` arguments must be plain object literals.
76
+
77
+ ---
78
+
79
+ ## Common Bugs & Pitfalls
80
+
81
+ - **[HIGH]** `useFetch` URL built from a reactive variable passed as a plain string instead of a getter → `useFetch("/api/items/" + route.params.id)` evaluates once at setup; the URL does not update when the param changes. Use a getter function: `useFetch(() => "/api/items/" + route.params.id)`.
82
+ - **[HIGH]** `window`, `document`, or `localStorage` accessed at the top level of a composable or `setup()` → `ReferenceError` during SSR because those globals do not exist on the server. Guard with `if (import.meta.client) { ... }` or move access inside `onMounted`.
83
+ - **[HIGH]** `navigateTo()` called in route middleware without `return` → the middleware continues executing after the redirect fires, leading to unexpected side effects or double navigation. Always `return navigateTo(...)`.
84
+ - **[HIGH]** `useState` initial value provided as an inline object or array literal rather than a factory function → the same object reference is shared across concurrent SSR requests, causing state leakage between users. Always use a factory function: `useState("key", () => ({}))`.
85
+ - **[HIGH]** `useFetch` / `useAsyncData` result used immediately without checking `pending` → `data` is `null` on the first render while the fetch is in-flight; accessing nested properties on it throws. Always check `pending.value` or use optional chaining until data resolves.
86
+ - **[MEDIUM]** `useRoute()` / `useRouter()` called inside `server/api/` or `server/middleware/` handlers → these composables are client/Nuxt-app-scoped and return the wrong or empty route in server context. Use `event.context.params`, `getQuery(event)`, and `getRouterParam(event, "id")` from H3 instead.
87
+ - **[MEDIUM]** `` error in a `server/api/` handler not caught → unhandled promise rejection crashes the Nitro worker and returns a generic 500 to the client. Wrap in try/catch and use `createError({ statusCode, message })` to return structured error responses.
88
+ - **[MEDIUM]** Conditional rendering based on `process.server` that differs between SSR and client hydration → Vue's hydration algorithm detects the mismatch and throws; in severe cases the entire DOM is re-rendered. Replace with `<ClientOnly>` or ensure both server and client render the same initial HTML.
89
+ - **[MEDIUM]** Plugin accessing `useRouter()` before the router is ready during app boot → router is `undefined` at that point causing a runtime error. Use `app.router` from the plugin `nuxtApp` argument or call router-dependent code inside `nuxtApp.hook("app:mounted", ...)`.
90
+ - **[LOW]** Changes to `nuxt.config.ts` (modules, plugins, build options) not reflected in the running dev server → Nuxt does not always hot-reload config changes. Restart the dev server after any `nuxt.config.ts` modification.
91
+ - **[LOW]** `useHead` called multiple times in the same component with overlapping keys → the last call wins and earlier declarations are silently overridden. Consolidate into a single `useHead` call per component.
@@ -0,0 +1,54 @@
1
+ # OpenAI API — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: `from 'openai'`, `import openai`, `OpenAI()`, `client.chat.completions`, `OPENAI_API_KEY`, `gpt-4`, `gpt-3.5`, `dall-e`
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[CRITICAL]** `OPENAI_API_KEY` included in client-side JavaScript bundles or mobile app binaries → key extractable by any user, enabling unauthorized API usage at the application's expense. Move all API calls server-side; never expose the key to the client.
10
+ - **[HIGH]** Raw user input passed as the `content` of the system message → prompt injection allows users to override system instructions or leak the system prompt. Treat user content as untrusted; separate it clearly from system instructions and validate structure.
11
+ - **[HIGH]** LLM output rendered directly as HTML or passed to `eval()` without sanitization → XSS or code injection if the model generates malicious content. Sanitize all model output before rendering; never use `innerHTML` or `eval` with LLM responses.
12
+ - **[HIGH]** Project API key not used; account-level key with full access granted instead → compromise of one service exposes all projects and billing. Use project-scoped keys with the minimum required model and endpoint permissions.
13
+ - **[MEDIUM]** Conversation history containing user PII stored without encryption or a defined retention policy → privacy regulation violation. Encrypt stored conversations and enforce a deletion schedule aligned with your privacy policy.
14
+ - **[MEDIUM]** Function calling tools configured with dangerous side effects (file deletion, sending emails, database writes) without a human-in-the-loop confirmation step → agent autonomously takes destructive actions. Require explicit user confirmation before executing irreversible tool calls.
15
+
16
+ ---
17
+
18
+ ## Performance
19
+ - **[HIGH]** Chat completions not streamed (`stream: false`) for responses that users wait for → UI appears frozen until the full response is returned. Use `stream: true` with server-sent events or WebSocket to emit tokens progressively.
20
+ - **[HIGH]** `max_tokens` not set → model generates the maximum possible response length regardless of task needs, inflating cost and latency. Set `max_tokens` to a reasonable upper bound for the specific task.
21
+ - **[HIGH]** No request-level or semantic caching for prompts with repeated or identical inputs → every request hits the API, accumulating unnecessary cost. Implement exact-match caching (Redis) for deterministic prompts; use OpenAI's prompt caching for long system prompts.
22
+ - **[MEDIUM]** `gpt-4` (or `gpt-4o`) used for simple classification or extraction tasks where `gpt-4o-mini` is sufficient → 10-25x higher cost with no quality benefit. Profile task requirements and choose the smallest model that meets the quality bar.
23
+ - **[MEDIUM]** Batch API not used for offline processing jobs → synchronous requests at standard pricing for workloads that tolerate async processing. Use the Batch API for 50% cost reduction on non-latency-sensitive tasks.
24
+ - **[LOW]** `logprobs` not requested when the application needs uncertainty estimation → downstream logic cannot assess model confidence. Request `logprobs: true` and `top_logprobs` when the application needs to branch on model certainty.
25
+
26
+ ---
27
+
28
+ ## Architecture
29
+ - **[HIGH]** API calls made directly from the frontend application → API key exposed, no server-side rate limiting, no cost controls per user. Create a backend proxy endpoint that authenticates users, applies rate limits, and forwards requests to OpenAI.
30
+ - **[HIGH]** No fallback model or provider configured → primary model unavailability causes complete feature outage. Implement a fallback chain: primary model to a smaller model or alternative provider using the same interface.
31
+ - **[MEDIUM]** System prompts and message templates hardcoded as string literals in application code → prompts not versioned, reviewed, or A/B testable. Extract prompts to dedicated template files or a prompt management system with version control.
32
+ - **[MEDIUM]** No per-user or per-session token budget enforced → a single user can consume the entire API quota. Track token usage per user with a counter in Redis or a database and enforce soft/hard limits.
33
+ - **[MEDIUM]** Requests and responses not logged for debugging and quality monitoring → impossible to diagnose failures or improve prompts. Log sanitized request/response pairs (with PII removed) to an observability platform.
34
+ - **[LOW]** API version not pinned in the client configuration → a model deprecation or API change silently breaks the application. Pin the model name and use a specific API version header; subscribe to OpenAI's deprecation notifications.
35
+
36
+ ---
37
+
38
+ ## Code Quality
39
+ - **[HIGH]** No error handling for `RateLimitError`, `APIConnectionError`, `APIStatusError` → transient errors surface directly to users as unhandled exceptions. Wrap all API calls in a try/except block and handle each error type with appropriate user messaging or retry.
40
+ - **[HIGH]** No retry logic with exponential backoff for rate limit and server errors → temporary API issues cause permanent request failures. Implement retry with jitter, honouring the `Retry-After` response header when present.
41
+ - **[MEDIUM]** `temperature` set to a fixed value regardless of use case → high temperature for factual extraction produces hallucinations; low temperature for creative tasks produces repetitive output. Set `temperature` per task type: near 0 for factual/structured, 0.7-1.0 for creative.
42
+ - **[MEDIUM]** System prompt not tested against injection attempts → adversarial inputs bypass system instructions in production. Test the system prompt with known injection patterns and add instruction hierarchy enforcement.
43
+ - **[MEDIUM]** TypeScript response types not validated at runtime → `response.choices[0].message.content` accessed without null checking, crashing when the API returns an unexpected shape. Use Zod or the SDK's typed interfaces and validate before accessing nested fields.
44
+ - **[LOW]** `presence_penalty` and `frequency_penalty` not considered for long-form generation tasks → repetitive output degrades quality in summaries or stories. Tune these parameters based on the output domain.
45
+
46
+ ---
47
+
48
+ ## Common Bugs & Pitfalls
49
+ - **[HIGH]** `finish_reason: 'length'` not checked → truncated responses treated as complete, producing partial JSON, cut-off sentences, or incomplete function calls. Always check `finish_reason` and handle `'length'` by requesting continuation or increasing `max_tokens`.
50
+ - **[HIGH]** Function/tool call response not checked for `finish_reason: 'tool_calls'` before parsing `tool_calls` → accessing `tool_calls` when `finish_reason` is `'stop'` causes a null dereference. Gate all `tool_calls` parsing behind a `finish_reason === 'tool_calls'` check.
51
+ - **[MEDIUM]** Token counting implemented with a character-based heuristic instead of the correct tokenizer → prompts silently exceed context limits at the API boundary. Use the `tiktoken` library with the model-specific encoding for accurate token counting.
52
+ - **[MEDIUM]** File or image uploads not validated for size and MIME type before sending → requests rejected by the API with a cryptic error after upload. Validate file size against API limits and allowed content types client-side before uploading.
53
+ - **[MEDIUM]** `message.content` accessed directly when the response may be a tool call message → `content` is `null` for `assistant` messages with `tool_calls`. Check the `role` and `finish_reason` before accessing `content`.
54
+ - **[LOW]** `seed` parameter not used in tests → non-deterministic responses make automated testing of prompt behaviour unreliable. Set `seed` to a fixed value in tests to increase response consistency.
@@ -0,0 +1,54 @@
1
+ # OpenGL — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: `#include <GL/`, `#include <OpenGL/`, `glCreateShader`, `glDrawElements`, `glBindBuffer`, `GLSL`, `*.vert`, `*.frag`, OpenGL with C/C++/Python
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[HIGH]** GLSL shader source compiled from user-provided or network-fetched strings → malformed shader source can trigger GPU driver crashes, hangs, or denial of service; only compile shader source from trusted, reviewed files bundled with the application.
10
+ - **[HIGH]** Buffer sizes not validated before passing to `glBufferData()` or `glBufferSubData()` → oversized uploads may corrupt driver memory or cause undefined behavior; validate buffer sizes against allocated storage before every upload.
11
+ - **[MEDIUM]** OpenGL error checking (`glGetError()`) not performed after resource creation calls → resource creation failures silently return invalid handles (0), causing subtle corruption later; check errors after every `glGen*`/`glCreate*` call in debug builds.
12
+ - **[LOW]** Debug output (`GL_DEBUG_OUTPUT`) not stripped or gated in release builds → verbose driver messages may leak internal renderer details; disable or redirect debug output in production.
13
+
14
+ ---
15
+
16
+ ## Performance
17
+ - **[CRITICAL]** Draw calls not batched → each `glDrawElements()` or `glDrawArrays()` call carries CPU-to-GPU synchronization overhead; batch geometry into a single VBO/VAO per material and use instanced rendering (`glDrawElementsInstanced`) for repeated geometry.
18
+ - **[HIGH]** `glGetError()` called every frame inside the render loop → synchronizes CPU and GPU pipeline, causing a stall; use `GL_DEBUG_OUTPUT` with a callback for development and remove all `glGetError()` from the hot render path.
19
+ - **[HIGH]** Vertex Array Objects (VAOs) not used or re-specified per draw call → full vertex attribute state must be re-configured each time; bind a VAO per unique vertex layout and reuse it.
20
+ - **[HIGH]** Vertex buffer data uploaded every frame for geometry that does not change → unnecessary CPU-GPU transfers; use `GL_STATIC_DRAW` hint for immutable geometry and only call `glBufferSubData()` for the portion that changes.
21
+ - **[HIGH]** Textures not using mipmaps for geometry rendered at variable distances → cache misses and aliasing artifacts on distant surfaces; generate mipmaps with `glGenerateMipmap()` and set `GL_TEXTURE_MIN_FILTER` to a mipmap mode.
22
+ - **[MEDIUM]** Uniform variables updated individually with repeated `glUniform*` calls instead of using Uniform Buffer Objects (UBOs) → excessive CPU-GPU round trips for shared per-frame data; pack shared uniforms (view/projection matrices, lights) into a UBO bound once per frame.
23
+ - **[MEDIUM]** Overdraw not managed (multiple opaque fragments rendered per pixel) → fragment shader runs on pixels whose result is discarded by a later opaque draw; sort opaque objects front-to-back and use early-Z testing.
24
+ - **[LOW]** Shader programs not shared between objects that use the same material → redundant `glUseProgram()` calls and duplicated GPU program state; sort draw calls by shader program to minimize state changes.
25
+
26
+ ---
27
+
28
+ ## Architecture
29
+ - **[HIGH]** OpenGL state machine state not tracked at the application level → leftover bindings from one draw pollute the next; maintain an explicit state cache or use a DSA (Direct State Access) approach to minimize implicit state.
30
+ - **[HIGH]** GPU resource handles (textures, buffers, shaders, framebuffers) not managed with RAII wrappers → handles leak when exceptions occur or ownership is unclear; wrap all handles in RAII classes with destructors that call the corresponding `glDelete*`.
31
+ - **[MEDIUM]** Raw OpenGL calls scattered throughout application code instead of behind a renderer abstraction → tightly coupled to OpenGL, impossible to port to Vulkan or Metal; abstract the GPU API behind a renderer interface.
32
+ - **[MEDIUM]** Shader source GLSL `#version` directive not matching the OpenGL context version → shaders may silently fall back to older GLSL or fail to compile; explicitly request the context version and match the `#version` directive.
33
+ - **[LOW]** No asset pipeline for compiling, validating, and caching SPIR-V or precompiled shaders → shaders compiled from source at startup every run; precompile shaders and cache binary programs with `glProgramBinary()`.
34
+
35
+ ---
36
+
37
+ ## Code Quality
38
+ - **[HIGH]** `glDeleteBuffers()`, `glDeleteTextures()`, `glDeleteShader()`, and `glDeleteProgram()` not called for every created resource → GPU memory and handle leaks accumulate over the application lifetime; audit every `glGen*`/`glCreate*` call for a corresponding `glDelete*`.
39
+ - **[HIGH]** Shader compilation result not checked with `glGetShaderiv(shader, GL_COMPILE_STATUS, &status)` → compilation errors silently produce an invalid program; always check compile and link status and retrieve the info log on failure.
40
+ - **[MEDIUM]** `GL_DEBUG_OUTPUT` not enabled during development → driver-level errors and performance warnings go undetected; enable with `glEnable(GL_DEBUG_OUTPUT)` and `glDebugMessageCallback()` in debug builds.
41
+ - **[MEDIUM]** Attribute and uniform locations hardcoded as integer literals instead of queried with `glGetAttribLocation()`/`glGetUniformLocation()` → breaks silently when the shader is modified; query locations after program linking and cache them.
42
+ - **[MEDIUM]** Fragment shader computing values that could be computed in the vertex shader → unnecessary per-fragment computation; move lighting precomputation and invariant calculations to the vertex stage where possible.
43
+ - **[LOW]** Y-axis direction inconsistency between OpenGL NDC (Y-up) and image coordinate space (Y-down) not handled → textures appear upside down; flip V coordinate in texture UVs or flip the image on load.
44
+
45
+ ---
46
+
47
+ ## Common Bugs & Pitfalls
48
+ - **[HIGH]** `glBindBuffer(GL_ARRAY_BUFFER, 0)` called before `glVertexAttribPointer()` → the VAO records a null buffer binding for that attribute → geometry invisible or garbage on screen; always bind the VBO before calling `glVertexAttribPointer()`.
49
+ - **[HIGH]** Clip-space coordinates outside NDC range `[-1, 1]` in X, Y, and `[0, 1]` or `[-1, 1]` in Z depending on depth range → geometry silently clipped and invisible; verify projection matrix parameters produce correct NDC for the scene's near/far planes.
50
+ - **[MEDIUM]** Texture coordinate `(0, 0)` assumed to be at top-left but OpenGL places it at bottom-left → textures rendered upside down; flip the V coordinate in UVs (`v_flipped = 1.0 - v`) or load images with vertical flip enabled.
51
+ - **[MEDIUM]** Depth buffer not included in `glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)` each frame → z-buffer retains previous frame's values → z-fighting and incorrect depth test results; always clear both color and depth buffers.
52
+ - **[MEDIUM]** Framebuffer completeness not checked with `glCheckFramebufferStatus()` after setup → incomplete framebuffer silently renders nothing; always check and log framebuffer status after attaching all textures and renderbuffers.
53
+ - **[LOW]** Front-face winding order (`GL_CW` vs `GL_CCW`) set inconsistently with asset exporter settings → back-face culling removes the wrong faces, making geometry invisible from the front; verify winding order matches the DCC tool's export convention.
54
+ - **[LOW]** `glEnable(GL_DEPTH_TEST)` not called when depth testing is needed → all fragments pass depth test regardless of Z value → incorrect overdraw; explicitly enable depth testing and set `glDepthFunc(GL_LESS)`.
@@ -0,0 +1,54 @@
1
+ # Phaser — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: `phaser`, `from 'phaser'`, `Phaser.Game`, `new Phaser.Game`, `this.physics`, `this.add.sprite`, `preload()`, `create()`, `update()`
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[HIGH]** Asset URLs constructed from user-provided input and passed to `this.load.image()` or `this.load.atlas()` → loading arbitrary remote assets; validate all asset URLs against an allowlist before loading.
10
+ - **[HIGH]** `new Function()` or `eval()` used for mod scripting or dynamic game logic → arbitrary JavaScript execution in the browser; use a sandboxed expression evaluator or a restricted scripting API.
11
+ - **[MEDIUM]** Game state (score, level, items) stored only in client-side `localStorage` or URL hash → trivially modified by players; validate all meaningful state server-side for competitive or monetized games.
12
+ - **[MEDIUM]** WebSocket game server accepting move/action messages without server-side legality checks → cheating by sending crafted messages; authoritative server must validate every action independently.
13
+ - **[LOW]** Third-party Phaser plugins loaded from untrusted CDN without subresource integrity (SRI) hashes → supply-chain attack via CDN compromise; pin plugin versions and add `integrity` attributes.
14
+
15
+ ---
16
+
17
+ ## Performance
18
+ - **[HIGH]** Sprites and images not packed into texture atlases → one draw call per sprite; use `this.load.atlas()` with a texture packer tool to batch rendering into a single draw call.
19
+ - **[HIGH]** `this.add.sprite()`, `this.add.image()`, or `this.physics.add.*()` called inside `update()` → creates new game objects every frame; create objects once in `create()` and reuse via an object pool.
20
+ - **[HIGH]** Physics bodies attached to all game objects including static decorative elements → unnecessary physics simulation cost; only enable physics on objects that require collision or movement.
21
+ - **[MEDIUM]** Expensive calculations (pathfinding, distance checks across all entities) run every frame in `update()` → CPU bottleneck; throttle with frame counters or use Phaser's `Time.addEvent` for periodic checks.
22
+ - **[MEDIUM]** Camera not configured with `roundPixels: true` for tile-based or pixel-art games → sub-pixel rendering causes blurry, shimmering sprites; enable `roundPixels` in the game config.
23
+ - **[MEDIUM]** Tween animations re-created every time they play instead of using `TweenManager` restart → unnecessary allocation; create tweens once and call `tween.restart()`.
24
+ - **[LOW]** `antialias: true` (Phaser default) used for pixel-art games → bilinear filtering blurs pixel art; set `antialias: false` and `pixelArt: true` in game config.
25
+
26
+ ---
27
+
28
+ ## Architecture
29
+ - **[HIGH]** All game logic implemented in a single Scene class → God object that is impossible to test or extend; split into multiple Scenes (Boot, Preload, Menu, Game, UI) and use Phaser's Scene plugin system for shared services.
30
+ - **[HIGH]** Game state not managed via Phaser's Scene lifecycle → ad-hoc flags and globals shared between scenes; use `this.scene.start('GameScene', data)` to pass state and separate scene responsibilities cleanly.
31
+ - **[MEDIUM]** Cross-object communication done via direct method calls or global variables instead of Phaser's event emitter → tight coupling; use `this.events.emit()` / `this.events.on()` or the global `this.game.events` bus.
32
+ - **[MEDIUM]** Asset keys hardcoded as repeated magic strings (`'player'`, `'bullet'`) across multiple files → typo bugs cause silent load failures; centralize keys in a constants module.
33
+ - **[MEDIUM]** Input handling scattered across multiple `update()` functions without a dedicated input manager → inconsistent state; centralize input in a dedicated handler class or Scene plugin.
34
+ - **[LOW]** Manual animation loops in `update()` incrementing frame counters instead of using Phaser's `AnimationManager` → duplicated logic; define animations with `this.anims.create()` and play by key.
35
+
36
+ ---
37
+
38
+ ## Code Quality
39
+ - **[HIGH]** Assets not loaded in `preload()` before being used in `create()` or `update()` → runtime errors when texture key is missing; load all required assets in `preload()` with proper load-error handlers.
40
+ - **[MEDIUM]** Arrow functions not used for callbacks that reference `this` (e.g., `this.input.on('pointerdown', function() { this.jump() })`) → `this` is `undefined` or `window`; use arrow functions or `.bind(this)`.
41
+ - **[MEDIUM]** Physics overlap or collider callbacks referencing objects that may have been destroyed → callback fires on a destroyed object; check `gameObject.active` inside collision callbacks before processing.
42
+ - **[MEDIUM]** `this.scene.pause()` or `this.scene.stop()` not cleaning up event listeners added via `this.input.on()` → listeners persist across scene restarts; remove listeners in the Scene's `shutdown` event handler.
43
+ - **[LOW]** Debug physics rendering (`debug: true` in arcade physics config) left enabled in production → visible collision boxes and performance overhead; disable for non-development builds.
44
+
45
+ ---
46
+
47
+ ## Common Bugs & Pitfalls
48
+ - **[HIGH]** `this.add.image()` or `this.add.sprite()` called inside `update()` → thousands of overlapping game objects created in seconds; always create display objects in `create()` and toggle visibility instead.
49
+ - **[HIGH]** Input event listeners (`this.input.on(...)`) added in `create()` not removed in the Scene `shutdown` event → listeners survive Scene restarts, resulting in multiple handlers firing per event; clean up in `this.events.on('shutdown', ...)`.
50
+ - **[MEDIUM]** Texture key used in `this.add.sprite(x, y, 'key')` not matching the key used in `this.load.image('key', url)` → sprite renders as invisible or shows missing-texture error; keys must match exactly (case-sensitive).
51
+ - **[MEDIUM]** Camera bounds not set with `this.cameras.main.setBounds()` in tiled worlds → camera follows player off the edge of the map; always set bounds to match tilemap dimensions.
52
+ - **[MEDIUM]** `this.physics.add.group()` objects not recycled with `group.killAndHide()` + `group.get()` pattern → pool not used, defeating its purpose; use `classType` on the group and `get()`/`killAndHide()` for pooling.
53
+ - **[LOW]** `setInteractive()` not called on a game object before registering pointer events → pointer callbacks silently never fire; always call `setInteractive()` before `on('pointerdown', ...)`.
54
+ - **[LOW]** Scene `data` not cleared between runs → stale state from a previous play-through bleeds into the next; reset all scene-local state in the `create()` method or pass fresh data via `this.scene.restart(data)`.
@@ -0,0 +1,55 @@
1
+ # Phoenix — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: Phoenix, use Phoenix.Router, use Phoenix.Controller, use Phoenix.LiveView, mix phx, *.ex with Plug
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[CRITICAL]** Missing authentication plug before protected pipelines → unauthenticated requests reach protected controllers and LiveViews. Add an auth plug to the `:browser` or `:api` pipeline before route scopes that require authentication.
10
+ - **[CRITICAL]** CSRF token verification disabled or bypassed on form submissions → cross-site requests processed without token validation. Never disable `Plug.CSRFProtection`; ensure forms include the CSRF meta tag.
11
+ - **[CRITICAL]** SQL injection via raw Ecto queries with string interpolation → attacker-controlled input alters query structure. Always use parameterized Ecto queries; never interpolate user input directly into query strings.
12
+ - **[HIGH]** `send_file` with a user-controlled path → attacker crafts a path to read arbitrary server files. Validate and sanitize file paths; serve files from a whitelist of allowed directories.
13
+ - **[MEDIUM]** Missing Content Security Policy headers → XSS attacks can execute arbitrary scripts in the browser. Configure CSP headers in the Plug pipeline or via the `Phoenix.Controller.put_secure_browser_headers/2` helper.
14
+ - **[HIGH]** LiveView event handlers not verifying user identity → any authenticated user can trigger events belonging to other users. Validate that `socket.assigns.current_user` has permission for every handle_event action.
15
+ - **[HIGH]** Not scoping Ecto queries to the current user → users can access or modify other users data by guessing IDs (IDOR). Always filter queries with the current user context (e.g., `from(p in Post, where: p.user_id == ^user_id)`).
16
+ - **[MEDIUM]** Storing sensitive data in LiveView socket assigns → assigns visible in client-side inspection of LiveView state. Store minimal data in assigns; keep sensitive state server-side and use opaque references.
17
+
18
+ ---
19
+
20
+ ## Performance
21
+ - **[HIGH]** N+1 queries from Ecto associations not preloaded with `Repo.preload` → one query per record executed in a loop, causing exponential DB load. Use `Repo.preload/2` or eager-load associations in the initial query.
22
+ - **[MEDIUM]** LiveView sending full page state on every change instead of targeted `push_event` → excessive data transferred over the WebSocket on minor updates. Use granular diff-based updates and `push_event` for targeted client updates.
23
+ - **[HIGH]** No PubSub topic cleanup on LiveView termination → subscriptions accumulate in memory after a client disconnects, causing a slow memory leak. Unsubscribe from PubSub topics in the `terminate/2` callback.
24
+ - **[HIGH]** Missing database indexes for columns used in frequent WHERE and JOIN clauses → full table scans on every query degrade performance as data grows. Add indexes via Ecto migrations for all frequently filtered columns.
25
+ - **[MEDIUM]** Presence tracking on high-traffic channels without cleanup → stale presence entries accumulate when clients disconnect unexpectedly. Configure Phoenix Presence with appropriate TTLs and handle disconnects.
26
+ - **[HIGH]** Not paginating large Ecto query results → loading thousands of records into memory on a single request causes latency spikes and OOM risk. Apply `limit/offset` or cursor-based pagination on all list queries.
27
+
28
+ ---
29
+
30
+ ## Architecture
31
+ - **[HIGH]** Business logic in controllers instead of Phoenix context modules → controllers become fat and domain logic is scattered, making testing difficult. Move data access and business rules into context modules (e.g., `Accounts`, `Posts`).
32
+ - **[HIGH]** Ecto changesets not used for all data validation → data inserted or updated without field-level validation, allowing invalid data into the DB. Use changesets for every data modification path, including internal ones.
33
+ - **[MEDIUM]** Channels not scoped to per-user or per-resource topics → broadcasts reach unintended subscribers, leaking data across user sessions. Namespace topics with user or resource identifiers (e.g., `"room:"+room_id`).
34
+ - **[MEDIUM]** LiveView handle_event growing too large with many clauses → event handler becomes hard to navigate and test. Extract complex event handling into delegate functions or separate LiveComponent modules.
35
+ - **[MEDIUM]** Not using Phoenix.Token for signed, time-limited tokens in URLs → unsigned tokens can be forged or reused. Use `Phoenix.Token.sign/3` and `Phoenix.Token.verify/4` for URL-embedded tokens.
36
+
37
+ ---
38
+
39
+ ## Code Quality
40
+ - **[HIGH]** Missing changeset validations, relying only on database constraints → invalid data generates DB exceptions instead of user-friendly validation errors. Define all validation rules in changesets before DB insertion.
41
+ - **[MEDIUM]** Not using the with macro for multi-step operations that can fail → nested case statements for sequential operations become deeply indented and error-prone. Use with to chain operations and handle errors at one exit point.
42
+ - **[MEDIUM]** Not using Phoenix.Token for signed data passed to clients (e.g., invite links, email verification) → unsigned tokens can be tampered with. Use Phoenix.Token.sign and verify with appropriate max_age.
43
+ - **[MEDIUM]** LiveView assigns not correctly populated via assign/3 or assign_new/3 → accessing undefined assigns in templates raises a KeyError. Always initialize all assigns in mount and update them via assign/3.
44
+ - **[LOW]** Not using LiveView streams for large lists → full list sent to client on every update, increasing bandwidth. Use LiveView stream/3 for efficient, incremental list updates.
45
+ - **[MEDIUM]** Returning large binaries from controllers without streaming → entire response buffered in memory. Use send_chunked/2 or serve large files directly with send_file/3.
46
+
47
+ ---
48
+
49
+ ## Common Bugs & Pitfalls
50
+ - **[HIGH]** Repo.get! raising Ecto.NoResultsError in a controller → unhandled exception returns a 500 instead of a 404. Use Repo.get/3 and handle the nil case explicitly, returning a 404 response.
51
+ - **[CRITICAL]** LiveView socket not authenticated in mount/3 → the LiveView is accessible to unauthenticated users. Always check socket.assigns.current_user in mount and redirect if not authenticated.
52
+ - **[HIGH]** Phoenix.PubSub broadcast without topic namespacing → messages reach unintended subscribers across different users or tenants. Always include a user or resource identifier in the topic string.
53
+ - **[HIGH]** Ecto cast/3 vs change/2 confusion → using change bypasses field whitelisting, allowing arbitrary field updates. Use cast/3 with an explicit list of permitted fields for all user-supplied data.
54
+ - **[MEDIUM]** PubSub subscriptions not re-established after LiveView reconnect → subscriptions lost after a client reconnect, causing missed messages. Re-subscribe in the handle_params or mount callback on reconnect.
55
+ - **[MEDIUM]** Unhandled {:error, changeset} in context functions → callers receive a match error instead of a structured error response. Always pattern-match on {:ok, result} and {:error, changeset} in controllers.
@@ -0,0 +1,56 @@
1
+ # PHP — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: *.php, <?php, $_GET, $_POST, $_SESSION, echo, mysqli_, PDO::, composer.json
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[CRITICAL]** SQL injection via string concatenation in database queries → attacker alters query structure to exfiltrate or modify data. Always use PDO or MySQLi with prepared statements and bound parameters.
10
+ - **[CRITICAL]** XSS via echo $userInput without htmlspecialchars() → attacker injects arbitrary HTML and JavaScript into rendered pages. Escape all user-controlled output with htmlspecialchars($var, ENT_QUOTES, UTF-8).
11
+ - **[HIGH]** Missing CSRF tokens on state-mutating forms → cross-site form submissions accepted without validation. Generate a CSRF token per session, include it in every form, and verify it on POST/PUT/DELETE.
12
+ - **[CRITICAL]** File inclusion with user input (include($_GET[page]) or require($_REQUEST[file])) → remote or local file inclusion allows code execution or arbitrary file read. Never use user input in include/require; use a whitelist map of allowed page identifiers.
13
+ - **[CRITICAL]** eval() with user-provided data → arbitrary PHP code execution on the server. Remove all eval() calls; restructure to avoid dynamic code evaluation.
14
+ - **[CRITICAL]** $_FILES upload without MIME type and extension validation → attacker uploads a PHP file with an image extension and executes it. Validate file type with finfo_file(), restrict allowed extensions, and store uploads outside the web root.
15
+ - **[HIGH]** Session fixation not mitigated with session_regenerate_id() after login → attacker forces a known session ID on the victim, gaining access after they authenticate. Call session_regenerate_id(true) immediately after successful authentication.
16
+ - **[HIGH]** Sensitive data (passwords, API keys) stored in plaintext in database → DB breach exposes all credentials. Hash passwords with password_hash($pass, PASSWORD_ARGON2ID) and verify with password_verify().
17
+
18
+ ---
19
+
20
+ ## Performance
21
+ - **[HIGH]** N+1 database queries executed in loops → one query per iteration causes exponential DB load as dataset grows. Fetch related data in a single query using JOINs or batch lookups and map results in PHP.
22
+ - **[HIGH]** OPcache not enabled or misconfigured → PHP bytecode recompiled on every request, adding significant CPU overhead. Enable and configure OPcache with appropriate memory and file limits in php.ini.
23
+ - **[MEDIUM]** Large arrays processed without generators → entire dataset loaded into memory at once, causing high memory pressure. Use generators (yield) for lazy iteration over large datasets.
24
+ - **[HIGH]** Catastrophic backtracking regex patterns in preg_match or preg_replace → malformed input causes exponential execution time, enabling ReDoS. Audit regex patterns for nested quantifiers and test with adversarial inputs; set PCRE limits.
25
+ - **[MEDIUM]** Not using prepared statements for all queries, relying on string escaping instead → mysql_real_escape_string and similar functions are fragile substitutes. Use PDO with parameterized queries consistently for all database access.
26
+ - **[MEDIUM]** Outputting large amounts of HTML without output buffering control → slow page generation time before first byte sent to client. Use ob_start()/ob_end_flush() or streaming output strategies for large responses.
27
+ - **[HIGH]** Heavy computation (image processing, PDF generation) done synchronously in request handlers → slow response times and PHP-FPM worker pool exhaustion. Offload heavy tasks to a job queue (e.g., Beanstalkd, Redis Queue).
28
+
29
+ ---
30
+
31
+ ## Architecture
32
+ - **[HIGH]** Procedural code without separation of concerns (DB, business logic, HTML in one file) → code is untestable and unmaintainable. Adopt MVC or at minimum separate data access, logic, and presentation into distinct files.
33
+ - **[HIGH]** Mixing HTML markup and business logic in the same PHP file → presentation and logic changes affect each other, and XSS risks increase. Use templates (Twig, Blade) or at minimum separate view files from PHP logic.
34
+ - **[MEDIUM]** Not using Composer autoloading, relying on manual require chains instead → missing includes cause fatal errors; dependency management is ad hoc. Configure Composer autoloading and use PSR-4 namespacing for all classes.
35
+ - **[HIGH]** Global variables used for state sharing across functions and files → globals create hidden coupling and make testing impossible. Pass dependencies explicitly through function parameters or a service container.
36
+ - **[MEDIUM]** No environment-specific configuration management → production credentials hardcoded in source or committed to version control. Use environment variables or a .env file (loaded with vlucas/phpdotenv) and gitignore it.
37
+
38
+ ---
39
+
40
+ ## Code Quality
41
+ - **[HIGH]** error_reporting(0) or display_errors=On in production → errors suppressed in development hide real bugs; errors displayed in production expose internals to attackers. Set error_reporting(E_ALL) in development, log errors to a file in production.
42
+ - **[HIGH]** @ error suppression operator hiding real errors → genuine errors silently swallowed, making debugging extremely difficult. Remove all @ suppression; handle potential errors explicitly with try/catch or conditional checks.
43
+ - **[HIGH]** Type juggling with == instead of === leading to unexpected comparisons → 0 == false, "" == null, and similar comparisons produce incorrect logic. Use strict comparison === throughout; be explicit about types in all comparisons.
44
+ - **[CRITICAL]** magic_quotes_gpc or register_globals enabled (legacy PHP) → magic quotes corrupt data and create false security; register_globals exposes request data as variables. Disable both in php.ini; migrate away from any code relying on them.
45
+ - **[MEDIUM]** Not using strict_types declaration in PHP 7+ code → implicit type coercion causes unexpected behavior in function calls. Add declare(strict_types=1) at the top of every PHP file.
46
+ - **[LOW]** Not following PSR-12 or a consistent coding standard → inconsistent style makes code harder to review and maintain. Enforce a coding standard with PHP-CS-Fixer or PHPCS in the CI pipeline.
47
+
48
+ ---
49
+
50
+ ## Common Bugs & Pitfalls
51
+ - **[CRITICAL]** Type juggling authentication bypass: 0 == false or md5(password) == 0 pattern → loose comparison allows forged tokens to match any hash starting with 0e. Always use === for security-sensitive comparisons and hash_equals() for timing-safe comparison.
52
+ - **[LOW]** array_map vs array_walk confusion → array_map returns a new array while array_walk modifies in place; incorrect usage produces subtle bugs. Know the distinction: use array_map for transforming values, array_walk for side effects.
53
+ - **[MEDIUM]** intval() overflow on 32-bit systems for large IDs → IDs above PHP_INT_MAX wrap to negative values, causing incorrect queries or logic. Use 64-bit builds for production and handle large integers as strings when necessary.
54
+ - **[MEDIUM]** Timezone not configured via date_default_timezone_set() or php.ini → date and time calculations differ across servers, causing scheduling and expiry bugs. Set the timezone explicitly to UTC in application bootstrap.
55
+ - **[HIGH]** Not verifying file upload success before processing with move_uploaded_file() → partially uploaded or failed uploads processed as valid files, causing errors or overwriting data. Always check $_FILES[error] === UPLOAD_ERR_OK before processing.
56
+ - **[MEDIUM]** Using md5() or sha1() for password hashing → both are cryptographically broken and fast, enabling brute-force attacks. Replace with password_hash($pass, PASSWORD_ARGON2ID) and password_verify().
@@ -0,0 +1,86 @@
1
+ # Playwright / Cypress Stack Rules
2
+
3
+ Detection signals: `playwright.config.*`, `cypress.config.*`, `*.cy.js` / `*.cy.ts`, `*.spec.ts` with `page.goto`, `test(` + `expect(page`, `cy.` commands, `@playwright/test` in deps, `cypress` in deps
4
+
5
+ ---
6
+
7
+ ## Security
8
+
9
+ - **[HIGH]** Hardcoded test credentials (passwords, API keys, tokens) in test files committed to VCS → secrets in git history are permanent. Use environment variables or a `.env.test` file excluded from git via `.gitignore`.
10
+ - **[HIGH]** Tests hitting a production URL or production API endpoints → risk of mutating real data, polluting analytics, triggering real emails, or billing real users. Enforce a test-only base URL in config and assert on startup.
11
+ - **[HIGH]** Tests running as highly privileged users (admin, root) for all scenarios → permission bugs are invisible when every test bypasses authorization. Create role-specific test users and test each role explicitly.
12
+ - **[MEDIUM]** Auth tokens or session cookies stored in Playwright `storageState` files committed to VCS → valid session tokens leak in git history. Add all `storageState` and `.auth/` files to `.gitignore`.
13
+ - **[MEDIUM]** Test user with admin privileges used for all tests → over-privileged tests hide permission and RBAC bugs. Create role-specific test accounts and verify access control boundaries explicitly.
14
+ - **[MEDIUM]** Test videos or screenshots containing sensitive data (PII, tokens in URLs) uploaded to public CI artifacts → data exposure in artifact storage visible to all repo members. Restrict artifact visibility or scrub sensitive fields before capture.
15
+ - **[LOW]** No IP or domain allowlist on test environment → anyone with the test URL can interact with the system during a test run. Restrict access by IP or require auth even on test environments.
16
+ - **[LOW]** Using real third-party OAuth flow in tests → tests depend on external identity provider availability; tokens may have unintended side effects. Use a mock OAuth server or pre-issued test tokens.
17
+ - **[LOW]** Not revoking or rotating test credentials after a CI breach → long-lived test credentials become attack vectors. Rotate test credentials on the same schedule as production secrets.
18
+ - **[LOW]** Browser contexts sharing cookies across test files in the same worker → cross-test auth state pollution causes intermittent failures. Always create a fresh browser context per test or use `test.use({ storageState: undefined })`.
19
+
20
+ ---
21
+
22
+ ## Performance
23
+
24
+ - **[CRITICAL]** `page.waitForTimeout(5000)` / `cy.wait(5000)` arbitrary sleeps in tests → tests are slow by a hardcoded amount and still flaky because the wait may be insufficient. Replace with `waitForResponse`, `waitForSelector`, or `expect(locator).toBeVisible()`.
25
+ - **[HIGH]** All tests running serially in a single worker → no parallelism; suite time scales linearly with test count. Use Playwright `--workers=4` or configure Cypress Cloud parallel execution.
26
+ - **[HIGH]** Logging in via the UI form in every test → login flow is slow (300-1000ms), depends on UI stability, and adds flakiness. Cache authenticated state with Playwright `storageState` or Cypress `cy.session()` and reuse across tests.
27
+ - **[HIGH]** Not reusing browser contexts across related tests → expensive browser context creation dominates test time. Use `test.use({ storageState })` and group tests that share auth state into the same file.
28
+ - **[HIGH]** No network interception to stub slow external APIs → tests wait for real API latency (100-2000ms per call). Use `page.route()` (Playwright) or `cy.intercept()` (Cypress) to return instant mock responses.
29
+ - **[MEDIUM]** Large fixture JSON files loaded in every test regardless of need → memory overhead and slower test initialization. Load only the subset of fixture data relevant to each test.
30
+ - **[MEDIUM]** No test sharding across CI machines → single-machine bottleneck means full suite runs on one agent. Use Playwright `--shard=1/4` flags or Cypress Cloud to distribute across agents.
31
+ - **[MEDIUM]** Capturing screenshots on every step rather than only on failure → disk I/O and artifact upload time wasted on passing tests. Set `screenshot: 'only-on-failure'` in config.
32
+ - **[MEDIUM]** Missing `baseURL` in config → full URLs hardcoded in every test are fragile on environment change. Set `baseURL` in `playwright.config.ts` and use relative paths in tests.
33
+ - **[LOW]** Not enabling Playwright trace files for debugging CI failures → binary search through logs is the only way to diagnose flaky CI failures. Enable `trace: 'on-first-retry'` in playwright.config.
34
+ - **[LOW]** Not using route interception to block slow third-party scripts → page load time inflated by analytics, ads, or chat widgets. Intercept and abort those requests in a global setup fixture.
35
+ - **[LOW]** Reinitializing test data via the UI instead of via direct API calls → slow and brittle data setup. Seed test data through API calls or database seed scripts in `beforeEach`.
36
+
37
+ ---
38
+
39
+ ## Architecture
40
+
41
+ - **[HIGH]** Tests depend on execution order -- later tests assume prior tests left the app in a specific state → shared DB state causes cascade failures when any test fails or runs in isolation. Each test must be independently self-contained.
42
+ - **[HIGH]** Test data not cleaned up between runs → state accumulates across runs causing intermittent failures. Use `beforeEach`/`afterEach` API cleanup, isolated test databases, or database snapshots restored per run.
43
+ - **[HIGH]** Selectors based on CSS classes (`.submit-btn`, `.btn-primary`) or auto-generated class names → breaks on any CSS refactoring or build tool change. Use `data-testid` attributes or semantic locators (`getByRole`, `getByLabel`).
44
+ - **[HIGH]** No Page Object Model or fixture abstraction for complex multi-step flows → duplicated selectors and navigation logic across files; one selector change requires global search-and-replace. Extract to page objects or Playwright fixtures.
45
+ - **[MEDIUM]** Test assertions missing after user actions (click, form submit, navigation) → test passes even if the action had no visible effect. Always assert the resulting state: URL changed, element appeared, data updated.
46
+ - **[MEDIUM]** Network route interception registered after the navigation that triggers the request → the request fires before the intercept is in place, so it hits the real server. Always register `page.route()` or `cy.intercept()` before `page.goto()` or `cy.visit()`.
47
+ - **[MEDIUM]** Test data created via UI flows instead of API calls → slow, brittle, and dependent on other UI features. Create prerequisite data via API calls in `beforeEach`; only test the feature under test through the UI.
48
+ - **[MEDIUM]** No explicit test environment configuration or assertion that tests are running against the correct environment → tests silently run against the wrong environment. Assert `baseURL` or environment flag in a global setup.
49
+ - **[MEDIUM]** Hard-coded waits used as a substitute for proper synchronization strategy → entire team copies the pattern, leading to an ever-growing suite of sleeping tests. Establish a team convention: always use locator-based assertions.
50
+ - **[LOW]** Missing `test.describe` grouping in large test files → flat list of 50+ tests is hard to navigate in reports and difficult to run selectively. Group by feature or user flow.
51
+ - **[LOW]** Not using Playwright fixtures for shared setup and teardown logic → `beforeEach` duplication across files drifts over time. Extract shared setup to typed Playwright fixtures.
52
+ - **[LOW]** No visual regression testing for critical UI components → layout regressions pass all functional tests. Add `expect(page).toHaveScreenshot()` for key screens with defined threshold.
53
+
54
+ ---
55
+
56
+ ## Code Quality
57
+
58
+ - **[CRITICAL]** No assertion after a user interaction (click, type, submit) → test passes even if the action had no effect or caused an error. Always assert the resulting observable state after every meaningful action.
59
+ - **[HIGH]** `cy.intercept` registered after `cy.visit` → the request is already in flight before the intercept; the stub is never used. Always register intercepts before the navigation that triggers the request.
60
+ - **[HIGH]** `test.only` / `it.only` / `cy.only` committed to the repository → all other tests are silently skipped in CI. Use grep patterns (`--grep`) for focused local runs; never commit `.only`.
61
+ - **[HIGH]** Hardcoded waits (`cy.wait(3000)`, `page.waitForTimeout`) replacing proper assertions → brittle and slow. Replace with `expect(locator).toBeVisible()` or `cy.contains(text).should(be.visible)`.
62
+ - **[MEDIUM]** `page.locator('text=Submit')` used for button interaction → brittle text matching that breaks on i18n and copy changes. Use `getByRole('button', { name: 'Submit' })` instead.
63
+ - **[MEDIUM]** Not using Playwright auto-retry assertions → manually polling or using `waitFor` loops instead of built-in retry. Use `expect(locator).toBeVisible({ timeout: 5000 })` which retries automatically.
64
+ - **[MEDIUM]** Missing retry configuration for tests that cover genuinely flaky network behavior → one-off network hiccups cause CI failures. Add `retries: 2` in playwright.config for specific projects.
65
+ - **[MEDIUM]** Assertions without meaningful failure messages → failures show raw selector strings with no context about expected behavior. Add `{ message: 'expected X after clicking Y' }` to assertion options.
66
+ - **[MEDIUM]** Not grouping related tests in `test.describe` → flat test files are hard to navigate in HTML reports and difficult to run selectively. Group by user story or page section.
67
+ - **[LOW]** Screenshot on failure not configured → no visual evidence of what the page looked like when a CI test failed. Add `screenshot: 'only-on-failure'` to playwright.config.
68
+ - **[LOW]** Not using Playwright codegen to record baseline selectors → hand-written selectors are error-prone. Record the flow with `npx playwright codegen` then refine the generated selectors.
69
+ - **[LOW]** Missing `expect.soft` for non-critical assertions in the same test → first failing assertion aborts the test; later assertions never run. Use `expect.soft` for independent checks in the same scenario.
70
+
71
+ ---
72
+
73
+ ## Common Bugs & Pitfalls
74
+
75
+ - **[HIGH]** `page.click()` on an element not in the viewport → click lands on a different element or fails silently. Playwright auto-scrolls in most cases, but verify with `expect(locator).toBeInViewport()` before asserting click effect.
76
+ - **[HIGH]** Clicking a button that is briefly disabled then re-enabled without waiting for the enabled state → race condition causes click on disabled element with no effect. Add `.should('not.be.disabled')` (Cypress) or `expect(btn).toBeEnabled()` (Playwright) before clicking.
77
+ - **[HIGH]** Missing `Promise.all` wrapping `page.waitForNavigation` and `page.click` → the navigation event fires before the wait is registered, causing the test to hang. Always use `Promise.all([page.waitForNavigation(), page.click(locator)])`.
78
+ - **[HIGH]** Cypress command subject lost after `.then()` → the subject chain resets inside `.then()`; chaining commands after it acts on the wrong subject. Store the result in a variable or use `.wrap()` to re-enter the Cypress chain.
79
+ - **[MEDIUM]** Confusing `toBeVisible()` with `toBeInViewport()` → `toBeVisible()` checks CSS display/visibility properties; the element may be off-screen and still pass. Use `toBeInViewport()` when you need to assert the element is on screen.
80
+ - **[MEDIUM]** Playwright `context.newPage()` not closed in test teardown → browser contexts accumulate in long test runs, consuming memory and file descriptors. Always call `page.close()` or `context.close()` in `afterEach`.
81
+ - **[MEDIUM]** Cypress fixture file path resolved relative to `cypress/fixtures` not the project root → wrong path causes file-not-found errors that are hard to diagnose. Always prefix with the fixture directory structure.
82
+ - **[MEDIUM]** Playwright `page.evaluate()` called with a non-serializable argument (class instance, function, Map) → the argument is silently dropped or causes a serialization error. Only pass JSON-serializable values to `page.evaluate()`.
83
+ - **[MEDIUM]** Not accounting for animations or transitions delaying element interactability → click or assertion fires while the element is mid-transition. Use `page.waitForFunction` or CSS transition duration in test environment set to 0.
84
+ - **[LOW]** Missing `{ failOnStatusCode: false }` when testing 4xx or 5xx responses in Cypress → Cypress fails the test on non-2xx status before any assertion runs. Pass `{ failOnStatusCode: false }` to `cy.request()`.
85
+ - **[LOW]** Not using Playwright `codegen` to record baseline user flows → hand-writing selectors is error-prone. Run `npx playwright codegen <url>` to auto-generate a starting test, then clean up.
86
+ - **[LOW]** Flaky tests marked as skipped or retried instead of fixed → retry masks systemic issues; skipped tests reduce coverage silently. Investigate root cause; fix race conditions or add proper wait conditions.
@@ -0,0 +1,60 @@
1
+ # PostgreSQL — Stack-Specific Review Rules
2
+
3
+ > Applies to: GR · SR · PR · AR · BR
4
+ > Detection signals: `pg`, `postgres`, `from 'pg'`, `psycopg2`, `asyncpg`, `pgx`, `DATABASE_URL` with `postgres://`, `*.sql` with PostgreSQL syntax
5
+
6
+ ---
7
+
8
+ ## Security
9
+ - **[CRITICAL]** SQL injection via string interpolation instead of parameterized queries (`$1`, `$2`) → attacker can read, modify, or delete any data. Use `$1`/`$2` placeholders with separate values array in all queries.
10
+ - **[CRITICAL]** Superuser (`postgres`) used for application connections → full DB access on compromise. Create a least-privilege application user with only required table permissions.
11
+ - **[HIGH]** `SECURITY DEFINER` functions without `SET search_path = schema, pg_catalog` → search_path injection allows privilege escalation. Always pin the search_path in SECURITY DEFINER functions.
12
+ - **[HIGH]** Row-level security (RLS) not enabled for multi-tenant data → any authenticated user can read all tenants' rows. Enable RLS and add policies per tenant identifier.
13
+ - **[HIGH]** Connection string with credentials in source code or logs → credentials exposed in version control or log aggregators. Use environment variables and ensure logging redacts connection strings.
14
+ - **[MEDIUM]** Unencrypted connections (no `sslmode=require`) to remote PostgreSQL → credentials and data sent in plaintext over network. Set `sslmode=require` (or `verify-full`) in all connection configs.
15
+ - **[MEDIUM]** Public schema permissions not revoked from the `public` role → any DB user can create objects in public schema. Run `REVOKE CREATE ON SCHEMA public FROM PUBLIC` after provisioning.
16
+ - **[MEDIUM]** `pg_stat_statements` view exposing query logs containing sensitive data → internal query patterns and data values visible to DB users with access. Restrict access to `pg_stat_statements` to DBA roles only.
17
+
18
+ ---
19
+
20
+ ## Performance
21
+ - **[HIGH]** Missing indexes on foreign key columns → sequential scan on every JOIN referencing that key, degrading at scale. Add a `CREATE INDEX` for each foreign key column not already covered by a unique constraint.
22
+ - **[HIGH]** `SELECT *` instead of specific columns → over-fetching wastes I/O and prevents index-only scans. Always select only the columns the application needs.
23
+ - **[HIGH]** N+1 queries — one query per row in a loop instead of a single JOIN or CTE → response time grows linearly with row count. Rewrite as a single query using JOIN, `IN`, or a CTE.
24
+ - **[HIGH]** Missing `EXPLAIN ANALYZE` review for queries reported as slow → root cause of slow queries unknown, fixes are guesswork. Run `EXPLAIN (ANALYZE, BUFFERS)` and inspect sequential scans and high actual rows.
25
+ - **[MEDIUM]** `LIKE '%pattern%'` without trigram index (`pg_trgm`) → forces full sequential scan for substring search. Install `pg_trgm` extension and create a GIN index on the column.
26
+ - **[MEDIUM]** Not using `RETURNING` clause after INSERT/UPDATE → requires a separate SELECT round-trip to fetch the generated ID or updated row. Add `RETURNING id` (or required columns) to INSERT/UPDATE statements.
27
+ - **[MEDIUM]** Large transactions holding locks for extended periods → concurrent writes block, causing queue build-up and timeouts. Break large batch operations into smaller chunks with intermediate commits.
28
+ - **[LOW]** No connection pooling (PgBouncer or application-level pool) → each new connection costs ~5-10 MB RAM; exhausted under load. Configure a connection pool sized to `(num_cores * 2) + disk_spindles`.
29
+
30
+ ---
31
+
32
+ ## Architecture
33
+ - **[HIGH]** Business logic duplicated in application layer that should be enforced in PostgreSQL constraints or triggers → logic can be bypassed by direct DB access or other services. Move invariants to CHECK constraints, triggers, or DB functions.
34
+ - **[HIGH]** No database migrations tool (Flyway, Liquibase, Alembic, golang-migrate) → schema drifts between dev/staging/production. Adopt a migrations tool and commit all migration files to version control.
35
+ - **[MEDIUM]** Not using schemas to namespace tables (e.g., `auth`, `billing`, `public`) → all tables in public schema creates naming conflicts and broad permission grants. Organize tables into schemas and grant per-schema privileges.
36
+ - **[MEDIUM]** Denormalized JSONB columns without GIN index or documented access pattern → arbitrary JSON stored without query performance consideration. Add GIN index (`jsonb_path_ops`) on JSONB columns used in WHERE clauses.
37
+ - **[MEDIUM]** UUID vs sequential ID choice not aligned with use case → random UUIDs cause index fragmentation; sequential IDs leak row counts. Use `gen_random_uuid()` (UUID v4) for external IDs; use `BIGSERIAL` for internal FKs.
38
+ - **[LOW]** Not using `ENUM` types for constrained string columns → invalid values accepted at application level only. Define PostgreSQL ENUM types or CHECK constraints to enforce value sets at the DB layer.
39
+
40
+ ---
41
+
42
+ ## Code Quality
43
+ - **[HIGH]** DDL changes applied directly to production database without a migration file → change cannot be reproduced, rolled back, or reviewed. All schema changes must go through a migration file committed to the repository.
44
+ - **[HIGH]** Missing `NOT NULL` constraints on columns the application always requires → NULLs accumulate silently, causing unexpected NullPointerExceptions in application code. Add `NOT NULL` to all columns with a non-optional meaning.
45
+ - **[MEDIUM]** Inconsistent column naming (mix of `snake_case` and `camelCase`) → ORM mapping errors and confusing SQL. Standardize on `snake_case` for all PostgreSQL identifiers.
46
+ - **[MEDIUM]** Missing `CHECK` constraints for data validation (e.g., `age > 0`, `status IN (...)`) → invalid data enters the DB and must be handled in every consumer. Add CHECK constraints to enforce domain rules at the storage layer.
47
+ - **[MEDIUM]** Foreign keys missing explicit `ON DELETE` / `ON UPDATE` behavior → accidental orphan rows or unintended cascades. Explicitly declare `ON DELETE RESTRICT`, `CASCADE`, or `SET NULL` based on the domain relationship.
48
+ - **[LOW]** No consistent timestamp columns (`created_at`, `updated_at`) on mutable tables → no audit trail for record lifecycle. Add `created_at TIMESTAMPTZ DEFAULT now()` and `updated_at TIMESTAMPTZ` with an update trigger.
49
+
50
+ ---
51
+
52
+ ## Common Bugs & Pitfalls
53
+ - **[HIGH]** Comparing nullable column with `= NULL` instead of `IS NULL` → expression always evaluates to NULL (never TRUE), silently returning wrong result set. Replace all `col = NULL` with `col IS NULL` and `col != NULL` with `col IS NOT NULL`.
54
+ - **[HIGH]** `TIMESTAMP` (without time zone) used for user-facing or cross-timezone data → stored value is ambiguous, causing daylight saving time bugs. Use `TIMESTAMPTZ` (timestamp with time zone) for all datetime columns.
55
+ - **[HIGH]** `INT` (32-bit) primary key on high-volume tables → overflows at ~2.1 billion rows, causing insert failures. Use `BIGINT` / `BIGSERIAL` or UUID for primary keys on tables expected to grow large.
56
+ - **[MEDIUM]** `VACUUM` not running (autovacuum disabled or tables excluded) → dead tuple bloat degrades query performance and index efficiency. Ensure autovacuum is enabled; monitor `pg_stat_user_tables.n_dead_tup`.
57
+ - **[MEDIUM]** `DISTINCT ON (col)` semantics misunderstood → which row is returned from each group is undefined without `ORDER BY`. Always pair `DISTINCT ON (col)` with `ORDER BY col, tie_breaker` to get deterministic results.
58
+ - **[MEDIUM]** `UPDATE`/`INSERT` inside a loop not wrapped in a transaction → each statement auto-commits, causing thousands of round trips and partial state on error. Wrap batch operations in `BEGIN` / `COMMIT`.
59
+ - **[LOW]** Unquoted mixed-case identifiers treated as lowercase → `CREATE TABLE "UserTable"` is different from `usertable`, causing "relation does not exist" errors. Use lowercase identifiers without quoting, or quote consistently everywhere.
60
+