@mrciphersmith/keryx 0.3.0 → 0.3.1

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 (89) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.js +8451 -4817
  3. package/dist/core.js +11700 -11374
  4. package/package.json +1 -1
  5. package/src/gdskills/bundled/agents/go-code-auditor.md +1 -1
  6. package/src/gdskills/bundled/agents/python-code-auditor.md +1 -1
  7. package/src/gdskills/bundled/install-manifest.json +271 -4
  8. package/src/gdskills/bundled/rules/core/model-selection.mdc +51 -0
  9. package/src/gdskills/bundled/skills/review/review-jev-rules/SKILL.md +267 -0
  10. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +26 -0
  11. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +1 -1
  12. package/src/gdskills/bundled/stacks/angular/agent-refs.json +4 -0
  13. package/src/gdskills/bundled/stacks/angular/governance/eval.json +1751 -0
  14. package/src/gdskills/bundled/stacks/angular/governance/scout.json +32 -0
  15. package/src/gdskills/bundled/stacks/angular/pack.json +55 -0
  16. package/src/gdskills/bundled/stacks/angular/rules/coding-style.mdc +82 -0
  17. package/src/gdskills/bundled/stacks/angular/rules/patterns.mdc +84 -0
  18. package/src/gdskills/bundled/stacks/angular/rules/security.mdc +70 -0
  19. package/src/gdskills/bundled/stacks/angular/rules/testing.mdc +73 -0
  20. package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/SKILL.md +127 -0
  21. package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/evals.json +72 -0
  22. package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/SKILL.md +98 -0
  23. package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/evals.json +73 -0
  24. package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/SKILL.md +112 -0
  25. package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/evals.json +74 -0
  26. package/src/gdskills/bundled/stacks/angular/skills/angular-testing/SKILL.md +102 -0
  27. package/src/gdskills/bundled/stacks/angular/skills/angular-testing/evals.json +71 -0
  28. package/src/gdskills/bundled/stacks/mobx/agent-refs.json +4 -0
  29. package/src/gdskills/bundled/stacks/mobx/governance/eval.json +904 -0
  30. package/src/gdskills/bundled/stacks/mobx/governance/scout.json +18 -0
  31. package/src/gdskills/bundled/stacks/mobx/pack.json +28 -0
  32. package/src/gdskills/bundled/stacks/mobx/rules/coding-style.mdc +91 -0
  33. package/src/gdskills/bundled/stacks/mobx/rules/patterns.mdc +122 -0
  34. package/src/gdskills/bundled/stacks/mobx/rules/security.mdc +56 -0
  35. package/src/gdskills/bundled/stacks/mobx/rules/testing.mdc +63 -0
  36. package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/SKILL.md +124 -0
  37. package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/evals.json +73 -0
  38. package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/SKILL.md +149 -0
  39. package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/evals.json +74 -0
  40. package/src/gdskills/bundled/stacks/nestjs/agent-refs.json +4 -0
  41. package/src/gdskills/bundled/stacks/nestjs/governance/eval.json +1308 -0
  42. package/src/gdskills/bundled/stacks/nestjs/governance/scout.json +34 -0
  43. package/src/gdskills/bundled/stacks/nestjs/pack.json +53 -0
  44. package/src/gdskills/bundled/stacks/nestjs/rules/coding-style.mdc +70 -0
  45. package/src/gdskills/bundled/stacks/nestjs/rules/patterns.mdc +83 -0
  46. package/src/gdskills/bundled/stacks/nestjs/rules/security.mdc +73 -0
  47. package/src/gdskills/bundled/stacks/nestjs/rules/testing.mdc +69 -0
  48. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/SKILL.md +157 -0
  49. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/evals.json +70 -0
  50. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/SKILL.md +129 -0
  51. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/evals.json +71 -0
  52. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/SKILL.md +143 -0
  53. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/evals.json +69 -0
  54. package/src/gdskills/bundled/stacks/nextjs-nuxt/agent-refs.json +4 -0
  55. package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/eval.json +2413 -0
  56. package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/scout.json +42 -0
  57. package/src/gdskills/bundled/stacks/nextjs-nuxt/pack.json +42 -0
  58. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/coding-style.mdc +69 -0
  59. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/patterns.mdc +88 -0
  60. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/security.mdc +72 -0
  61. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/testing.mdc +64 -0
  62. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/SKILL.md +147 -0
  63. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/evals.json +75 -0
  64. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/SKILL.md +118 -0
  65. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/evals.json +76 -0
  66. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/SKILL.md +135 -0
  67. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/evals.json +78 -0
  68. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/SKILL.md +116 -0
  69. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/evals.json +75 -0
  70. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/SKILL.md +134 -0
  71. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/evals.json +76 -0
  72. package/src/gdskills/bundled/stacks/vue/agent-refs.json +4 -0
  73. package/src/gdskills/bundled/stacks/vue/governance/eval.json +2215 -0
  74. package/src/gdskills/bundled/stacks/vue/governance/scout.json +42 -0
  75. package/src/gdskills/bundled/stacks/vue/pack.json +42 -0
  76. package/src/gdskills/bundled/stacks/vue/rules/coding-style.mdc +73 -0
  77. package/src/gdskills/bundled/stacks/vue/rules/patterns.mdc +84 -0
  78. package/src/gdskills/bundled/stacks/vue/rules/security.mdc +60 -0
  79. package/src/gdskills/bundled/stacks/vue/rules/testing.mdc +69 -0
  80. package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/SKILL.md +137 -0
  81. package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/evals.json +72 -0
  82. package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/SKILL.md +120 -0
  83. package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/evals.json +71 -0
  84. package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/SKILL.md +122 -0
  85. package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/evals.json +72 -0
  86. package/src/gdskills/bundled/stacks/vue/skills/vue-testing/SKILL.md +115 -0
  87. package/src/gdskills/bundled/stacks/vue/skills/vue-testing/evals.json +72 -0
  88. package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/SKILL.md +135 -0
  89. package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/evals.json +71 -0
@@ -0,0 +1,2413 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "reports": [
4
+ {
5
+ "schemaVersion": "1.0.0",
6
+ "skillId": "nextjs-nuxt/nextjs-nuxt-implementation",
7
+ "strictness": "high",
8
+ "trials": 10,
9
+ "triggerAccuracy": {
10
+ "truePositive": 8,
11
+ "falsePositive": 3,
12
+ "positives": 8,
13
+ "negatives": 6
14
+ },
15
+ "evidence": "authored",
16
+ "scenarios": [
17
+ {
18
+ "id": "trigger-positive-1",
19
+ "kind": "trigger-positive",
20
+ "prompt": "I need to add a new order-history page to my Next.js app router project, should the data fetching happen in a server component?",
21
+ "strictness": "high",
22
+ "trials": 1,
23
+ "passes": 1,
24
+ "passRate": 1,
25
+ "passAtK": 1,
26
+ "grader": "trigger-rank-fork-family",
27
+ "status": "ran",
28
+ "deterministic": true
29
+ },
30
+ {
31
+ "id": "trigger-positive-2",
32
+ "kind": "trigger-positive",
33
+ "prompt": "Write a Next.js server action that creates a post and revalidates the posts list",
34
+ "strictness": "high",
35
+ "trials": 1,
36
+ "passes": 1,
37
+ "passRate": 1,
38
+ "passAtK": 1,
39
+ "grader": "trigger-rank-fork-family",
40
+ "status": "ran",
41
+ "deterministic": true
42
+ },
43
+ {
44
+ "id": "trigger-positive-3",
45
+ "kind": "trigger-positive",
46
+ "prompt": "This Nuxt page needs to fetch orders on load, should I use useFetch or a plain fetch call?",
47
+ "strictness": "high",
48
+ "trials": 1,
49
+ "passes": 1,
50
+ "passRate": 1,
51
+ "passAtK": 1,
52
+ "grader": "trigger-rank-fork-family",
53
+ "status": "ran",
54
+ "deterministic": true
55
+ },
56
+ {
57
+ "id": "trigger-positive-4",
58
+ "kind": "trigger-positive",
59
+ "prompt": "I need an endpoint under server/api in my Nuxt app that hands back the logged-in user's profile data, can you build that?",
60
+ "strictness": "high",
61
+ "trials": 1,
62
+ "passes": 1,
63
+ "passRate": 1,
64
+ "passAtK": 1,
65
+ "grader": "trigger-rank-fork-family",
66
+ "status": "ran",
67
+ "deterministic": true
68
+ },
69
+ {
70
+ "id": "trigger-positive-5",
71
+ "kind": "trigger-positive",
72
+ "prompt": "Set up ISR on this Next.js product page so it revalidates every hour",
73
+ "strictness": "high",
74
+ "trials": 1,
75
+ "passes": 1,
76
+ "passRate": 1,
77
+ "passAtK": 1,
78
+ "grader": "trigger-rank-fork-family",
79
+ "status": "ran",
80
+ "deterministic": true
81
+ },
82
+ {
83
+ "id": "trigger-positive-6",
84
+ "kind": "trigger-positive",
85
+ "prompt": "Configure routeRules in nuxt.config.ts so this one Nuxt route renders as SPA-only",
86
+ "strictness": "high",
87
+ "trials": 1,
88
+ "passes": 1,
89
+ "passRate": 1,
90
+ "passAtK": 1,
91
+ "grader": "trigger-rank-fork-family",
92
+ "status": "ran",
93
+ "deterministic": true
94
+ },
95
+ {
96
+ "id": "trigger-positive-7",
97
+ "kind": "trigger-positive",
98
+ "prompt": "My Nuxt composable needs to call the API as soon as the page loads, how should I wire that up",
99
+ "strictness": "high",
100
+ "trials": 1,
101
+ "passes": 1,
102
+ "passRate": 1,
103
+ "passAtK": 1,
104
+ "grader": "trigger-rank-fork-family",
105
+ "status": "ran",
106
+ "deterministic": true
107
+ },
108
+ {
109
+ "id": "trigger-positive-8",
110
+ "kind": "trigger-positive",
111
+ "prompt": "I'm adding a new component to my Next.js app and I'm not sure which rendering boundary it belongs in, how do I decide?",
112
+ "strictness": "high",
113
+ "trials": 1,
114
+ "passes": 1,
115
+ "passRate": 1,
116
+ "passAtK": 1,
117
+ "grader": "trigger-rank-fork-family",
118
+ "status": "ran",
119
+ "deterministic": true
120
+ },
121
+ {
122
+ "id": "trigger-negative-1",
123
+ "kind": "trigger-negative",
124
+ "prompt": "Write a test for this Next.js page's server component",
125
+ "strictness": "high",
126
+ "trials": 1,
127
+ "passes": 0,
128
+ "passRate": 0,
129
+ "passAtK": 0,
130
+ "grader": "trigger-rank-fork-family",
131
+ "status": "ran",
132
+ "deterministic": true
133
+ },
134
+ {
135
+ "id": "trigger-negative-2",
136
+ "kind": "trigger-negative",
137
+ "prompt": "Review this Next.js server action diff for missing auth checks",
138
+ "strictness": "high",
139
+ "trials": 1,
140
+ "passes": 1,
141
+ "passRate": 1,
142
+ "passAtK": 1,
143
+ "grader": "trigger-rank-fork-family",
144
+ "status": "ran",
145
+ "deterministic": true
146
+ },
147
+ {
148
+ "id": "trigger-negative-3",
149
+ "kind": "trigger-negative",
150
+ "prompt": "Fix this Next.js build error about a Server Component importing useState",
151
+ "strictness": "high",
152
+ "trials": 1,
153
+ "passes": 0,
154
+ "passRate": 0,
155
+ "passAtK": 0,
156
+ "grader": "trigger-rank-fork-family",
157
+ "status": "ran",
158
+ "deterministic": true
159
+ },
160
+ {
161
+ "id": "trigger-negative-4",
162
+ "kind": "trigger-negative",
163
+ "prompt": "Migrate this Next.js app from the pages router to the app router",
164
+ "strictness": "high",
165
+ "trials": 1,
166
+ "passes": 0,
167
+ "passRate": 0,
168
+ "passAtK": 0,
169
+ "grader": "trigger-rank-fork-family",
170
+ "status": "ran",
171
+ "deterministic": true
172
+ },
173
+ {
174
+ "id": "trigger-negative-5",
175
+ "kind": "trigger-negative",
176
+ "prompt": "Add a plain React hook that debounces an input value, no routing involved",
177
+ "strictness": "high",
178
+ "trials": 1,
179
+ "passes": 1,
180
+ "passRate": 1,
181
+ "passAtK": 1,
182
+ "grader": "trigger-rank-fork-family",
183
+ "status": "ran",
184
+ "deterministic": true
185
+ },
186
+ {
187
+ "id": "trigger-negative-6",
188
+ "kind": "trigger-negative",
189
+ "prompt": "Fix this Go compile error about an unused import",
190
+ "strictness": "high",
191
+ "trials": 1,
192
+ "passes": 1,
193
+ "passRate": 1,
194
+ "passAtK": 1,
195
+ "grader": "trigger-rank-fork-family",
196
+ "status": "ran",
197
+ "deterministic": true
198
+ },
199
+ {
200
+ "id": "server-client-boundary-placement",
201
+ "kind": "behavior",
202
+ "prompt": "I have a Next.js app router page that shows an order summary with a 'mark as paid' button. Where should the client-side interactivity live, and how should the data get there?",
203
+ "strictness": "high",
204
+ "trials": 10,
205
+ "passes": 10,
206
+ "passRate": 1,
207
+ "passAtK": 1,
208
+ "grader": "regex+judge",
209
+ "status": "ran",
210
+ "trialRecords": [
211
+ {
212
+ "output": "# Order summary page: where `'use client'` goes and how data arrives\n\n## The short answer\n\nKeep the page itself a **Server Component**. Fetch the order server-side, and push `'use client'` down to the smallest leaf that actually needs it — in your case, the \"mark as paid\" button (or a tiny wrapper around it) — not the page, not the summary, not the button's parent grid.\n\n## How the data gets there\n\nFetch in the Server Component with the project's existing server-side data layer (or a direct `fetch()` if that's the convention in the repo), and pass the order down as **serializable props**:\n\n```\n// app/orders/[id]/page.tsx — Server Component (no directive)\nexport default async function OrderPage({ params }) {\n const order = await getOrder(params.id) // server-side; no secrets in the bundle\n if (!order) notFound()\n return (\n <main>\n <OrderSummary order={order} /> // plain server components\n <MarkPaidButton orderId={order.id} paid={order.paid} />\n </main>\n )\n}\n```\n\n`OrderSummary` renders on the server and ships zero JS for the read-only part of the page — that's the whole point of getting the boundary right. Only `MarkPaidButton` (or a small client subtree around it) carries `'use client'`.\n\nTwo things to avoid on the read path:\n\n- **Don't re-fetch the order client-side after mount.** The requirement is \"show this order,\" not \"react to a user changing a filter.\" A `useEffect` refetch here is a wasted round trip and a flash of loading/empty state for no reason.\n- **Don't mark the page `'use client'` to get access to `useState` for the button.** That pulls the entire order summary subtree into the browser bundle and gives up server rendering for content that never needed it.\n\n## The button itself\n\n```\n// MarkPaidButton.tsx\n'use client'\nimport { useFormStatus } from 'react-dom'\nimport { markPaid } from './actions' // server action\n\nexport function MarkPaidButton({ orderId, paid }: Props) {\n if (paid) return <span>Paid</span>\n return (\n <form action={markPaid}>\n <input type=\"hidden\" name=\"orderId\" value={orderId} />\n <Submit /> // uses useFormStatus for a pending state\n </form>\n )\n}\n```\n\nThe mutation lives in a Server Action (`'use server'`), not in a client handler — that keeps the write path on the server and lets you revalidate in the same round trip.\n\n## After the mutation\n\nIn the action, do the write and then revalidate the route(s) that display the order:\n\n```\n// actions.ts\n'use server'\nexport async function markPaid(formData: FormData) {\n // 1. authorize: does this session own this order?\n // 2. validate: orderId is a well-formed id we accept\n await db.orders.markPaid(orderId)\n revalidatePath(`/orders/${orderId}`)\n}\n```\n\n`revalidatePath` means the next render of that route serves the updated order automatically — **don't** bolt on a manual client refetch or optimistic-only UI to work around not having revalidated. (An optimistic pending state is fine for perceived speed; it isn't a substitute for the server actually seeing the change on the next request.)\n\n## What \"correct\" looks like here\n\n- Page and summary are Server Components; the browser bundle for this route is basically the button.\n- Data flows **server → serializable props → client leaf**; no client-side fetch on the read path.\n- The mutation is a Server Action that validates and authorizes its own input — the fact that the button is hidden when `paid` is true is UX, not a trust boundary. A Server Action is a directly reachable endpoint, so re-check ownership server-side inside it.\n- If `order` carries anything non-serializable (a DB client, a class instance), pass a plain shaped object down instead — Server→Client props must be serializable.\n\n## Verify before calling it done\n\n- `tsc --noEmit` and `next build` pass with no new \"needs `'use client'`\" or serialization warnings.\n- `MarkPaidButton`'s component file is the *only* new `'use client'` boundary — check it wasn't accidentally placed on `OrderSummary` or the page.\n- The action validates and authorizes `orderId` on the server, independent of whether the button was rendered.\n- No secret used by `getOrder`/`markPaid` has leaked into a `NEXT_PUBLIC_`-prefixed variable.\n\nThe underlying rule is in `rules/patterns.mdc` (boundary + rendering mode) and `rules/security.mdc` (action auth + env scoping) — worth a skim before you start if the repo has its own conventions for either.",
213
+ "outputSha256": "975d818fdd4ef91bbd353371adac2c43690357b2782668e50c9823876cefc34b",
214
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
215
+ "deterministic": [
216
+ true
217
+ ],
218
+ "judge": {
219
+ "verdict": "pass",
220
+ "reason": "Answer states page stays Server Component with no directive ('Keep the page itself a Server Component', code comment 'Server Component (no directive)'), places 'use client' only on MarkPaidButton leaf ('Only MarkPaidButton ... carries use client'), and passes order down as props (order={order}, orderId={order.id}). It explicitly warns against client re-fetch and against marking the page 'use client'."
221
+ },
222
+ "passed": true
223
+ },
224
+ {
225
+ "output": "Put the data-fetching in the Server Component page, and the interactivity in a small Client Component leaf that receives serializable props.\n\n**Where the interactivity lives**\n\nThe `page.tsx` for the order summary stays a Server Component (no directive). It fetches the order server-side — a direct call to your data layer or a `fetch()` — and renders the layout, heading, line items, totals, and the button. The only thing that needs `'use client'` is the \"mark as paid\" control itself: a small component (say `MarkAsPaidButton`) exported from its own file with `'use client'` at the top. That component owns the `useState` for pending/error state, the event handler, and the call to the Server Action. Do not put `'use client'` on the page, on a layout, or on a wrapper that also contains the summary markup — that would drag the static summary into the browser bundle for no reason.\n\n**How the data gets there**\n\nFetch the order in `page.tsx` during the server render. That gives you the summary HTML rendered on the server with no client fetch and no loading spinner. Pass only what the button needs down as props — typically `orderId`, and the current status if the button's label/disabled state depends on it. Props must be serializable (strings, numbers, plain objects), which an order id and a status string are.\n\nIf the order summary needs to reflect changes after the button is clicked, don't manage that by hand in the client. Let the Server Action revalidate: in `revalidatePath()` (or `revalidateTag()`) for the order's route, then the same request cycle re-renders the page server-side with the new status. The Client Component's job shrinks to: call the action, toggle its own pending state, surface an error if the action returns one. It should not itself fetch or cache the order.\n\n**The button's job, concretely**\n\n```\n'use client'\n// props: { orderId: string, status: 'unpaid' | 'paid' }\n// - disabled if status === 'paid'; label reflects status\n// - onClick (or form action) -> markOrderPaid(orderId)\n// - local pending/error state around the await\n```\n\nAnd the Server Action behind it (`'use server'`): re-validate the input server-side, authorize that this caller may mark *this* order paid, do the mutation, then `revalidatePath()` the order route. Treat it as a public endpoint — the client passing a valid-looking `orderId` is not authorization.\n\n**Two things to avoid**\n\nDon't mark the whole page `'use client'` \"to make the button work\" — that ships the entire summary and loses server rendering for content that never needed it. And don't fetch the order client-side after mount just because a Client Component exists; the Server Component already fetched it, so pass what's needed down and let revalidation refresh it after the mutation.\n\n**Verify before calling it done:** `tsc --noEmit` passes, `next build` shows no missing-boundary or serialization warnings, the Server Action validates and authorizes its own input independent of the client, and no secret (DB client, API key) is reachable from the button's import chain.",
226
+ "outputSha256": "b7ca80b4972d1b23d05f8c78ecccf21f253baa2b7f0497e51218be1cc1b2b4ce",
227
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
228
+ "deterministic": [
229
+ true
230
+ ],
231
+ "judge": {
232
+ "verdict": "pass",
233
+ "reason": "Criterion 1: 'page.tsx ... stays a Server Component (no directive)' and fetches order server-side. Criterion 2: \"only thing that needs 'use client' is the 'mark as paid' control itself: a small component (say MarkAsPaidButton)\". Criterion 3: 'Pass only what the button needs down as props — typically orderId, and the current status'; leaf 'should not itself fetch'. Fail criteria avoided: warns against page/layout 'use client' and client re-fetch."
234
+ },
235
+ "passed": true
236
+ },
237
+ {
238
+ "output": "Put the interactive button in its own small Client Component, and let the page stay a Server Component.\n\nConcretely:\n\n**Structure**\n\n```\napp/orders/[id]/page.tsx ← Server Component (no directive)\napp/orders/[id]/MarkPaidButton.tsx ← 'use client' leaf\n```\n\n**`page.tsx` — Server Component, no `'use client'`**\n\nFetch the order server-side with `fetch()` (or your project's server data layer), and pass only what the button needs down as serializable props:\n\n```tsx\nexport default async function OrderPage({ params }) {\n const order = await getOrder(params.id); // server-side fetch\n return (\n <section>\n <h1>Order {order.id}</h1>\n <dl>{/* summary fields... */}</dl>\n <MarkPaidButton orderId={order.id} paid={order.paid} />\n </section>\n );\n}\n```\n\nThe summary itself renders on the server — it never needs to ship to the browser as JS.\n\n**`MarkPaidButton.tsx` — the `'use client'` leaf**\n\n```tsx\n'use client';\n\nexport function MarkPaidButton({ orderId, paid }) {\n const [pending, startTransition] = useTransition();\n return (\n <form action={markPaid.bind(null, orderId)}>\n <button disabled={paid || pending}>\n {paid ? 'Paid' : pending ? 'Marking…' : 'Mark as paid'}\n </button>\n </form>\n );\n}\n```\n\nOr, if you need optimistic UI rather than a plain form: keep the `'use client'` component, call the Server Action from an `onClick` inside a `startTransition`, and use `useOptimistic` for the pending state.\n\n**The mutation — Server Action**\n\n```ts\n'use server';\n\nexport async function markPaid(orderId: string) {\n // 1. authorize the caller and load the order server-side\n // 2. re-validate state (e.g. not already paid, belongs to this user)\n // 3. write the update\n revalidatePath(`/orders/${orderId}`);\n}\n```\n\nThe `revalidatePath` call is what makes the page re-render with the fresh order in the same round trip — the Server Component re-fetches and the summary updates without any client-side refetch. Don't follow the action with a manual `fetch`/`refetch` from the client; that's a redundant second request and a common source of stale UI.\n\n**Key points**\n\n- The directive goes on the smallest leaf — the button — not on `page.tsx` or a layout. If the summary markup ends up inside the `'use client'` file, the boundary is too wide.\n- All data arrives via server-side fetch as serializable props. The button doesn't fetch anything on mount; it only triggers the mutation.\n- Validate and authorize inside the Server Action itself. It's a directly reachable endpoint, so client-side checks (if any) are UX only, not a trust boundary.\n- Any secrets / DB clients stay in the Server Action or server data layer and must not be reachable from the button's import chain.\n\n**Verify**\n\n`tsc --noEmit` and `next build` should both pass with no warning about a missing boundary or an invalid action reference, and clicking the button should update the summary via revalidation rather than a client refetch.",
239
+ "outputSha256": "47c1acddae4775a5890c77f1e787fb75655fe11d8d957ace8eca6a8347ada9a0",
240
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
241
+ "deterministic": [
242
+ true
243
+ ],
244
+ "judge": {
245
+ "verdict": "pass",
246
+ "reason": "page.tsx stays a Server Component with no 'use client' (criterion 1); 'use client' is placed only on MarkPaidButton.tsx leaf, explicitly 'not on page.tsx or a layout' (criterion 2); order fetched server-side and passed as props orderId/paid to the button, which 'doesn't fetch anything on mount' (criterion 3). No fail criteria: no whole-page/layout 'use client', no client refetch."
247
+ },
248
+ "passed": true
249
+ },
250
+ {
251
+ "output": "Put the interactivity at the smallest possible leaf, and let the page itself stay a Server Component.\n\nConcretely:\n\n**Page (Server Component, no directive)** — `app/orders/[id]/page.tsx` fetches the order server-side with `fetch()` (or your project's server data layer), then renders presentational pieces plus one small client leaf for the button. It passes only the serializable bits the button needs (order id, current paid status) as props. Do not mark the page `'use client'` — that would ship the whole order summary's JS to the browser and lose server rendering for content that never needed it.\n\n**Client leaf** — something like `components/mark-as-paid-button.tsx` with `'use client'` at the top. Keep it that small: it holds the pending/optimistic state and handles the click or form submit. It does not fetch the order; the data already arrived as props.\n\n**The mutation path** — a Server Action:\n\n```ts\n// app/orders/[id]/actions.ts\n'use server'\nexport async function markOrderPaid(orderId: string) {\n // re-authorize here: confirm the current user owns this order.\n // Client-side checks are UX only; this is a reachable endpoint.\n await db.orders.update({ where: { id: orderId }, data: { paid: true } })\n revalidatePath(`/orders/${orderId}`)\n}\n```\n\nThe button either submits a `<form action={markOrderPaid.bind(null, id)}>` (works even before hydration) or calls the action from its click handler. After the mutation, `revalidatePath` is what delivers fresh data — the Server Component re-renders with the new paid state in the same round trip. Do not add a client-side `useEffect` refetch after the action; that's an unnecessary second request and a common source of stale-UI bugs.\n\n**Where the state lives** — the interactive leaf can use `useFormStatus`/`useTransition` for the pending state. The authoritative \"is this paid\" value stays on the server and flows down as a prop; don't duplicate it into a client store.\n\nTwo things to watch:\n\n- Don't reach for `'use client'` on an ancestor (the layout or the summary component) just to get the button working — the directive only belongs on the leaf that needs `useState`/event handlers.\n- Re-validate and re-authorize inside the action. The button being hidden for a non-owner is not security; the action is a directly callable endpoint.\n\nIf your order summary has other interactive bits (a quantity selector, an expandable section), each gets its own small `'use client'` leaf rather than one shared client ancestor, so the rest of the page keeps its server rendering and stays out of the browser bundle.",
252
+ "outputSha256": "86999b803a8875414603a1882d0bae94d8efa2b7aade459286714083db376272",
253
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
254
+ "deterministic": [
255
+ true
256
+ ],
257
+ "judge": {
258
+ "verdict": "pass",
259
+ "reason": "Page stays Server Component with no directive ('Page (Server Component, no directive)'); 'use client' placed only on small leaf ('components/mark-as-paid-button.tsx' with 'use client' at the top); data passed as props ('passes only the serializable bits the button needs... as props', 'It does not fetch the order; the data already arrived as props'). Warns against ancestor 'use client' and client refetch."
260
+ },
261
+ "passed": true
262
+ },
263
+ {
264
+ "output": "Put the interactivity and the data on opposite sides of the Server/Client boundary, and keep that boundary as thin as possible.\n\n## Where the interactivity lives\n\nKeep `app/orders/[id]/page.tsx` as a **Server Component** (no `'use client'`). It should:\n\n- Fetch the order data itself, on the server.\n- Render all the static presentation — headings, line items, totals, status badge — as plain server-rendered JSX.\n- Drop a single small Client Component in for the button only.\n\nSo the only thing you mark `'use client'` is the button, e.g. `MarkAsPaidButton.tsx`. It holds the `useState` for pending/error state and the `onClick` handler. Do **not** mark the page, the summary layout, or any ancestor of the button — that would ship the whole summary's JS to the browser and lose server rendering for content that never needed it.\n\nIf the button is a real form submission (which \"mark as paid\" usually is), you often don't even need a Client Component at all — see below.\n\n## How the data gets there\n\n**Fetch on the server, pass down as props.** In the Server Component:\n\n```tsx\nconst order = await getOrder(params.id); // fetch() or your server data layer\n```\n\n`fetch()` in a Server Component is deduplicated and cached per render pass, so multiple components asking for the same order won't cause multiple requests. Pass only serializable props to the button (`orderId`, current status) — never a DB client or a secret.\n\n**Do not** re-fetch the order client-side after mount. The page already has it. The only client-side data flow that's justified here is the mutation result.\n\n## The mutation path\n\nUse a **Server Action** rather than a client `fetch` to an API route:\n\n```tsx\n// app/orders/[id]/actions.ts\n'use server'\nexport async function markAsPaid(orderId: string) {\n // authorize + validate orderId and the caller's session here,\n // server-side, regardless of what the client sent\n await db.orders.update(/* ... */);\n revalidatePath(`/orders/${orderId}`);\n}\n```\n\nThen in the page you can wire it straight to a form with no Client Component needed:\n\n```tsx\n<form action={markAsPaid.bind(null, order.id)}>\n <button type=\"submit\">Mark as paid</button>\n</form>\n```\n\nIf you want pending/optimistic UI, *then* extract a small Client Component that calls the action inside a `startTransition`/`useFormStatus`, but the boundary is still just the button.\n\nThe key point: `revalidatePath()` after the mutation means the same request cycle re-renders the page with fresh data. Don't hand-roll a client-side refetch of the order afterward — that's a redundant second request and a common source of stale UI.\n\n## The two things people get wrong here\n\n- **Marking the whole page `'use client'`** \"to make the button easier\" — ships the entire summary to the browser and drops SSR for static content. Isolate the directive to the leaf.\n- **Validating the amount/order state only on the client.** A Server Action is a directly reachable HTTP endpoint. Authorize the session and re-check that the order is in a valid state for payment *inside the action*, every time. Client-side checks are UX, not a trust boundary.\n\n## Verify before calling it done\n\n- `tsc --noEmit` passes and `next build` shows no missing-`'use client'`-boundary warnings.\n- No secret or DB client is reachable from the button's import chain.\n- The action validates and authorizes its own input independently of anything the client sent.",
265
+ "outputSha256": "1240844a5ea90396a38fcbd70caaa6d86d9f925ca23caddd3410d7f22ad51f63",
266
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
267
+ "deterministic": [
268
+ true
269
+ ],
270
+ "judge": {
271
+ "verdict": "pass",
272
+ "reason": "Answer keeps page.tsx as Server Component with no 'use client' (criterion 1), marks only MarkAsPaidButton.tsx 'use client' and warns against marking page/layout (criterion 2), and passes orderId/status as props from server parent, explicitly saying 'Do not re-fetch the order client-side' (criterion 3). No fail criteria hold."
273
+ },
274
+ "passed": true
275
+ },
276
+ {
277
+ "output": "Keep the page itself a Server Component. Here's the shape:\n\n**The page (server)**\n- Fetch the order server-side in the page — either directly with `fetch()` in the Server Component or through the project's existing server-side data layer. That fetch is deduped and cached per render pass, so you get the order data without a client round trip or a loading spinner.\n- Render the read-only summary markup here (line items, totals, status). None of that needs to ship JS.\n- Pass only the minimal serializable slice the button needs down to a small client leaf — typically `orderId` and maybe the current status.\n\n**The interactive leaf (client)**\n- Create a small `<MarkAsPaidButton orderId={...} />` with `'use client'` at the top of that one file. That's the boundary — not the page, not the layout, not the summary component. The directive belongs on the smallest leaf that actually needs `onClick`/state/`useState` for a pending spinner.\n- Put it inline where the button sits in the summary, so the rest of the tree stays server-rendered.\n\n**The mutation**\n- Wire the button to a Server Action (`'use server'`), either passed into the leaf as a prop or imported directly if the project does that. The action re-validates and authorizes `orderId` server-side — the button being rendered is not proof of anything, the action is a directly reachable endpoint.\n- After the write, call `revalidatePath()` (or `revalidateTag()` if the order is tagged) for the route(s) that show this order. The same request cycle then serves the updated status with the fresh data — no manual client refetch.\n- Client-side, the leaf only needs `useTransition`/`useFormStatus`-style pending state for button feedback. That's the entire client-side surface.\n\n**What not to do**\n- Don't mark the page or the summary `'use client'` to make the button work — that pulls the whole subtree (and its data-fetching concerns) into the browser bundle and loses the server render for content that never needed it.\n- Don't fetch the order client-side after mount and then render it — that's a spinner you don't need, since the server can render it during the initial request.\n- Don't hand-roll a client refetch of the order after the action; `revalidatePath` already covers it.\n\nSo the split is: data-fetching and all static markup stay on the server in the page; interactivity lives in one tiny `'use client'` button leaf that calls a revalidating Server Action.",
278
+ "outputSha256": "1c59ec8e50de2bd11cf52342700294556d415afc394d1eb551edc06bfc134bc6",
279
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
280
+ "deterministic": [
281
+ true
282
+ ],
283
+ "judge": {
284
+ "verdict": "pass",
285
+ "reason": "Answer states page stays Server Component fetching order server-side (\"Keep the page itself a Server Component\", \"Fetch the order server-side in the page\"). Names 'use client' only on small leaf: \"<MarkAsPaidButton orderId={...} /> with 'use client'... not the page, not the layout\". Passes data as prop: \"Pass only the minimal serializable slice... orderId\". Warns against page-level 'use client' and client refetch, not committing them."
286
+ },
287
+ "passed": true
288
+ },
289
+ {
290
+ "output": "Put the interactivity in a small client leaf, not the page.\n\n**Structure the page as a Server Component by default.** `app/orders/[id]/page.tsx` should have no `'use client'` directive. It fetches the order server-side (directly via your data layer or `fetch()`), then passes only what the interactive leaf needs down as serializable props:\n\n```\napp/orders/[id]/page.tsx <- Server Component, no directive\n └─ components/order-summary.tsx <- Server Component (pure presentational)\n └─ components/mark-as-paid-button.tsx <- the ONE 'use client' leaf\n```\n\n**Data flow.** The page fetches the order and renders the summary on the server — no client fetch-on-mount, no `useEffect` + loading spinner for data you already had at render time. It passes `orderId`, and maybe `isPaid`/`status`, into `<MarkAsPaidButton>`. That client component holds only the interaction state: a pending flag while the action runs, and maybe an optimistic \"paid\" state.\n\n**The mutation.** Make \"mark as paid\" a Server Action:\n\n```ts\n// app/orders/[id]/actions.ts\n'use server'\nexport async function markOrderAsPaid(orderId: string) {\n // 1. authorize: does this session own this order?\n // 2. validate orderId, check it's in a payable state\n // 3. write to DB\n revalidatePath(`/orders/${orderId}`)\n}\n```\n\nThe button calls it from an event handler (or wire it through a `<form action={markOrderAsPaid}>`). The `revalidatePath` is the key piece: the same request cycle re-renders the page server-side with fresh data, so the summary updates without you writing any client refetch logic. Don't hand-roll a `router.refresh()` or a second `fetch` afterward.\n\n**Do not** mark the whole page `'use client'` to avoid thinking about the boundary — that ships the entire summary subtree's JS and drops server rendering for content that never needed it. Keep the directive on the button only.\n\n**Two things to watch:**\n- Validate and authorize inside the action itself. The button is a directly reachable endpoint; disabling it client-side is UX, not a trust boundary.\n- If you want the button to feel instant, use `useOptimistic` in the client leaf rather than moving the fetch to the client — you keep server rendering and still get optimistic UI.\n\n**Verify** with `tsc --noEmit` and `next build`, checking there are no new \"missing `'use client'`\" or boundary warnings, and that the action rejects an order the session doesn't own.",
291
+ "outputSha256": "5adda51bfce927be06a4e39f10aae9b1294fd5655a1d8b18e7aa0be4c4c2c88f",
292
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
293
+ "deterministic": [
294
+ true
295
+ ],
296
+ "judge": {
297
+ "verdict": "pass",
298
+ "reason": "Answer states page.tsx is a Server Component with no 'use client' directive (criterion 1), names mark-as-paid-button.tsx as the ONE 'use client' leaf and warns against marking the whole page (criterion 2), and passes orderId/isPaid/status down as props with no client fetch-on-mount (criterion 3). No fail criteria hold."
299
+ },
300
+ "passed": true
301
+ },
302
+ {
303
+ "output": "Put `'use client'` on the smallest leaf that actually needs it — the button, not the page.\n\n**Structure**\n\n- `app/orders/[id]/page.tsx` stays a Server Component (no directive). It reads the route param, fetches the order server-side with `fetch()`, and passes serializable data down as props.\n- Extract the button into its own Client Component, e.g. `components/MarkAsPaidButton.tsx`, with `'use client'` at the top. It receives only what it needs (`orderId`, maybe `isPaid`) as plain props — no DB client, no secrets, no server-only imports.\n- The order summary itself (line items, totals, addresses) can stay server-rendered; only the button subtree ships JS to the browser. Don't mark the summary `'use client'` just because the button lives near it — import the button as a child instead.\n\n**How the data gets there**\n\n- Fetch in the Server Component with `fetch()`. It's deduplicated within a render pass and the result is serialized to the client as props.\n- Do **not** fetch the order client-side on mount. The button is an interaction, not a data-on-load concern — the page already has the data at render time.\n- For anything that changes the order after the mutation (e.g. a status badge elsewhere on the page), let `revalidatePath()` handle it rather than wiring a manual client refetch.\n\n**The mutation**\n\n- Put the mutation in a Server Action (`'use server'`, either inline in the page or in a `lib/actions/orders.ts`-style file).\n- In the button's Client Component, call it from a `onClick`/form submit and use `useTransition` (or `useActionState`) to disable the button while it's in flight.\n- Inside the action: re-check auth and validate `orderId` server-side, update the order, then call `revalidatePath('/orders/[id]', 'page')` (or `revalidateTag` for the order) so the re-render picks up the new `isPaid` state. The client validation on the button is UX only — the action is a reachable endpoint and must guard itself.\n\n**Checks before you're done**\n\n- `tsc --noEmit` and `next build` pass with no \"missing 'use client'\" or boundary warnings.\n- The button's import chain contains no server-only code (no `'use server'` files, DB clients, or `NEXT_PUBLIC_`-misplaced secrets).\n- The action validates and authorizes independently of the client.\n- If the route can tolerate cache staleness, set `export const revalidate = <seconds>` on the page instead of forcing it fully dynamic.",
304
+ "outputSha256": "2bb2fbb80755ba56dd780ef9adae020a997df4e87144aab9d42852a29c4d270d",
305
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
306
+ "deterministic": [
307
+ true
308
+ ],
309
+ "judge": {
310
+ "verdict": "pass",
311
+ "reason": "Answer states page.tsx stays a Server Component with no directive (\"stays a Server Component (no directive)\"), puts 'use client' only on the button leaf (\"smallest leaf... the button, not the page\"), and passes data as props (\"passes serializable data down as props\"; button \"receives only what it needs... as plain props\"). Explicitly warns against client-side fetch on mount. No fail criteria hold."
312
+ },
313
+ "passed": true
314
+ },
315
+ {
316
+ "output": "Put the interactivity in a small leaf Client Component, not the page. The page itself stays a Server Component.\n\n**Structure**\n\n```\napp/orders/[id]/page.tsx <- Server Component, async, no 'use client'\napp/orders/[id]/PayButton.tsx <- Client Component, 'use client'\n```\n\n`page.tsx` (no directive → Server Component):\n\n```tsx\nimport { getOrder } from '@/lib/orders';\nimport { PayButton } from './PayButton';\n\nexport default async function OrderPage({ params }: { params: { id: string } }) {\n const order = await getOrder(params.id); // runs on the server only\n return (\n <section>\n <h1>Order {order.id}</h1>\n <dl>{/* render summary from order directly */}</dl>\n <PayButton orderId={order.id} paid={order.paid} />\n </section>\n );\n}\n```\n\n`PayButton.tsx` (the only interactive leaf):\n\n```tsx\n'use client';\nimport { useTransition } from 'react';\nimport { markPaid } from './actions';\n\nexport function PayButton({ orderId, paid }: { orderId: string; paid: boolean }) {\n const [pending, start] = useTransition();\n if (paid) return <p>Paid</p>;\n return (\n <button disabled={pending} onClick={() => start(() => markPaid(orderId))}>\n Mark as paid\n </button>\n );\n}\n```\n\n**Why the boundary sits there**\n\nThe summary (names, amounts, dates, line items) has no interactivity — it should be server-rendered for the initial HTML and never ship as client JS. Only the button's click handling and pending state need the browser, so `'use client'` goes on the smallest component ancestor of that state. If you instead marked the whole page `'use client'`, you would pull the entire summary subtree into the browser bundle and lose server rendering for content that never needed it — that is exactly the Red Flags case.\n\n**How the data gets there**\n\n- Fetch in the Server Component (`getOrder(params.id)` / `fetch(...)` inside the page). `fetch` is deduped per render pass, and anything reading `cookies()`/`headers()`/`searchParams` makes the route dynamic automatically.\n- Pass only serializable props down to `PayButton` — here just `orderId` and `paid`. Do not pass the whole order object if the button only needs an id.\n- Do not re-fetch client-side after mount. The summary is server data; there is no post-interaction fetch requirement, so a client fetch would be a redundant second request.\n- Do not let `getOrder` or the DB client into `PayButton`'s import chain. Secrets and server-only modules stay on the server side of the boundary.\n\n**The mutation path**\n\n`markPaid` is a Server Action (`'use server'` at file top), called with `orderId`:\n\n```ts\n'use server';\nimport { revalidatePath } from 'next/cache';\nimport { auth } from '@/lib/auth';\nimport { markOrderPaid } from '@/lib/orders';\n\nexport async function markPaid(orderId: string) {\n const session = await auth();\n if (!session) throw new Error('unauthorized');\n if (typeof orderId !== 'string' || !orderId) throw new Error('bad input');\n await markOrderPaid(orderId, session.user.id); // authorizes ownership in the query\n revalidatePath(`/orders/${orderId}`);\n}\n```\n\nTwo things to note:\n\n- Re-validate and authorize inside the action itself. A Server Action is a directly reachable HTTP endpoint; you cannot trust that the call came from your button, so client-side checks are UX only, not a trust boundary.\n- After the mutation, call `revalidatePath` for the route that displays the changed data so the same request cycle serves fresh data. Do not hand-roll a client refetch after the action resolves — that is the \"just refetch client-side\" trap and a source of stale-UI bugs.\n\n**Rendering mode**\n\nLeave the route static unless it reads request-scoped data. If `getOrder` reads `cookies()`/`headers()` or you use `searchParams`, it is already dynamic — no flag needed. If the summary can tolerate staleness, prefer `export const revalidate = 30` (ISR) over forcing fully dynamic rendering. An order detail page keyed by id, showing a payment status, is usually a good candidate for a short revalidate window rather than `dynamic = 'force-dynamic'`.\n\n**Verification before calling it done**\n\n- `tsc --noEmit` clean, and `next build` produces no missing-`'use client'` or boundary warnings.\n- `markPaid` authorizes and validates independently of the button.\n- No secret leaked via a mis-scoped `NEXT_PUBLIC_` var or a server-only module imported into `PayButton`.",
317
+ "outputSha256": "75bda6994140bf7b41162568deccf314efc5ecad6c22376d22cb46b8f16bcf3e",
318
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
319
+ "deterministic": [
320
+ true
321
+ ],
322
+ "judge": {
323
+ "verdict": "pass",
324
+ "reason": "Answer states page.tsx stays a Server Component with no 'use client' (\"no directive → Server Component\"), puts 'use client' only on PayButton.tsx leaf, and passes orderId/paid as props from the server parent. It explicitly warns against marking the whole page 'use client' and against client re-fetching. All pass criteria hold; no fail criteria."
325
+ },
326
+ "passed": true
327
+ },
328
+ {
329
+ "output": "Keep the page itself a Server Component and push `'use client'` down to the smallest leaf that actually needs interactivity — the \"mark as paid\" button (or a tiny `<MarkPaidButton orderId={...} />` wrapper around it). The page never gets the directive just because one control is interactive.\n\nData flow:\n\n1. `page.tsx` (Server Component) fetches the order summary directly — a server-side `fetch()`/DB call, not a client hook — and renders the summary markup on the server.\n2. It passes only what the button needs down as serializable props: `orderId` (and whatever the mutation requires), not the whole order object if the button doesn't use it.\n3. The button is a Client Component with `'use client'` at the top of its own file. It can use `useState`, `useFormStatus`/`useTransition`, `onClick`, etc., but it does not fetch the order itself. Its `fetch`-ing job is done.\n4. The mutation is a Server Action (`'use server'`), either inline or in a separate file. The button invokes it from a form `action` or an event handler. Inside the action, validate and authorize server-side — it's a reachable HTTP endpoint, so the server check is the real one.\n5. After the write succeeds, call `revalidatePath()` for this order's route (or `revalidateTag()` if the order is tagged). That refreshes the Server Component's data in the same round trip. Do not hand-roll a client-side refetch of the order after the action resolves.\n\nSo the boundary is: server fetches and renders the summary, client owns exactly the click/optimistic-pending UI, and the Server Action + `revalidatePath` closes the loop back into the server-rendered summary.\n\nOne anti-pattern to avoid here: don't mark the whole page `'use client'` \"so the button works,\" and don't `fetch` the order in a client `useEffect` on mount to avoid deciding the boundary — both defeat the server rendering the summary already has.",
330
+ "outputSha256": "d89180e005a857d640c20eef51a1bcf10bb9588ab926314950774ddf4bc30339",
331
+ "promptSha256": "6f9820f456a2ff0bee5bcb7f1234400793a7ae8dc14f3ae7d8d32d591a6e82ed",
332
+ "deterministic": [
333
+ true
334
+ ],
335
+ "judge": {
336
+ "verdict": "pass",
337
+ "reason": "Answer states page stays Server Component with no directive ('Keep the page itself a Server Component', 'The page never gets the directive'), places 'use client' only on the small button leaf ('push 'use client' down to the smallest leaf... the mark as paid button'), and passes data as props ('passes only what the button needs down as serializable props: orderId'), explicitly saying the button 'does not fetch the order itself'. Anti-patterns only warned against."
338
+ },
339
+ "passed": true
340
+ }
341
+ ]
342
+ },
343
+ {
344
+ "id": "nuxt-composable-setup-context",
345
+ "kind": "behavior",
346
+ "prompt": "In my Nuxt page, I want to fetch the current user's orders as soon as the page mounts, using a composable I wrote. Where should I call it?",
347
+ "strictness": "high",
348
+ "trials": 10,
349
+ "passes": 10,
350
+ "passRate": 1,
351
+ "passAtK": 1,
352
+ "grader": "regex+judge",
353
+ "status": "ran",
354
+ "trialRecords": [
355
+ {
356
+ "output": "Call it synchronously at the top level of `<script setup>`, not inside `onMounted` — even though \"as soon as the page mounts\" is exactly the phrasing that pulls you toward `onMounted`, that's the trap here.\n\nNuxt's data-fetching layers (`useFetch`, `useAsyncData`, and the typical custom composable that wraps them) rely on Nuxt's injection context, which is resolved synchronously when the composable is invoked during setup. If you call it inside `onMounted`:\n\n- it runs only on the client, so the orders never render during SSR (you get a blank/flash-then-fill), and\n- if the composable internally touches `useFetch`/`useAsyncData`/`useNuxtApp`, it will throw or warn about being called outside the proper context.\n\nSo the correct placement:\n\n```vue\n<script setup lang=\"ts\">\n// top level, synchronous — not inside onMounted\nconst { data: orders, pending, error } = useMyOrders()\n</script>\n```\n\nThis fires during SSR (on the server) and during hydration (on the client, reusing the same payload) without a second request, which is what you actually want for \"current user's orders as soon as the page mounts.\"\n\nA few things to check on your composable itself:\n\n- **Stable explicit key** — if it wraps `useFetch`/`useAsyncData`, give it a stable key (e.g. `'my-orders'`, or keyed by user id if it varies) so it dedupes correctly across server and client.\n- **Auth/context** — if \"current user\" depends on the request (a session cookie), make sure the composable reads that via `useRequestHeaders`/`useRequestFetch` on the server side, otherwise the server render won't see the user's cookie and you'll get an unauthenticated result that then hydrates stale. If that's a real concern in your app, it's worth confirming how the existing data-fetching composables in this project handle request-bound auth before you model yours on top of them.\n- **Only genuinely post-interaction, client-only calls** belong in `onMounted` — e.g. a \"load more\" click or a browser-only API. Initial page data does not.\n\nOne caveat: I can't see your composable. If it's a thin wrapper around `$fetch` (not `useFetch`/`useAsyncData`) and doesn't touch Nuxt's injection context, technically it would run inside `onMounted` without erroring — but you'd still lose SSR and pay an extra client round trip, so top-level setup is still the right home for it.",
357
+ "outputSha256": "edf7c54df02c94f4c6efa42eb634beb5a763ff9a8e934065cdc58cba71b85c36",
358
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
359
+ "deterministic": [
360
+ true
361
+ ],
362
+ "judge": {
363
+ "verdict": "pass",
364
+ "reason": "Answer states call it synchronously at top level of <script setup>, not inside onMounted (shows code `const { data: orders } = useMyOrders()` at top level). Explains Nuxt's injection context is resolved synchronously when composable is invoked during setup, so onMounted loses SSR/context. Fail criterion not met: onMounted only warned against, not recommended."
365
+ },
366
+ "passed": true
367
+ },
368
+ {
369
+ "output": "Call it synchronously at the top level of `<script setup>` — not inside `onMounted` or any other callback.\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = await useOrders()\n</script>\n```\n\nWhy not `onMounted`:\n\n- Nuxt composables like `useFetch`/`useAsyncData` (and any custom composable built on them) resolve their Nuxt injection context **synchronously at call time**. Calling one inside `onMounted` — or after any `await`, or inside an event handler — breaks that resolution and you get \"Nuxt instance unavailable\" or silent loss of SSR/hydration support.\n- `onMounted` only runs client-side, so calling there means the data is fetched *after* hydration in a second round trip, and it renders as empty on first paint. Calling at setup time lets the data be fetched during SSR and hydrated without a refetch.\n\nSo: `const { data } = await useOrders()` at the top level of `<script setup>`. If your composable needs a reactive argument (a filter, an id), pass it directly — `useFetch` tracks reactive keys itself:\n\n```ts\nconst status = ref<'open' | 'closed'>('open')\nconst { data: orders } = await useOrders(status) // refetch tracks status\n```\n\nTwo caveats worth checking in your own composable:\n\n1. **Give it a stable explicit key** if it uses `useAsyncData` internally (e.g. `'user-orders'`), so server and client dedupe and hydrate consistently instead of producing a mismatch warning.\n2. **Only use `onMounted` when the call is genuinely post-interaction and client-only** — e.g. \"load more\" pagination, or a search triggered by typing. For \"as soon as the page mounts,\" that's exactly what setup-time `useFetch`/`useAsyncData` is for, and you shouldn't drop to `onMounted` plus a bare `$fetch`.\n\nIf your composable was written assuming it would be called from `onMounted` (e.g. it does its own `$fetch` internally and returns plain refs), the fix is to move that `$fetch` into `useAsyncData`/`useFetch` inside the composable so it works at setup time — see `rules/patterns.mdc` for the idiom. Then verify with `nuxi typecheck` and `nuxi build` (watch for \"composable called outside of setup\" or hydration-mismatch warnings).",
370
+ "outputSha256": "9fa644553183f980ca1b742b3b943e77a9c439b2609c5c8aacd6ba4173b26dcd",
371
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
372
+ "deterministic": [
373
+ true
374
+ ],
375
+ "judge": {
376
+ "verdict": "pass",
377
+ "reason": "Answer says call it synchronously at top level of <script setup>, not inside onMounted/callback/after await (code shows `await useOrders()` at top level). Explains Nuxt composables resolve injection context synchronously at call time, so deferred calls break resolution ('Nuxt instance unavailable'). onMounted only mentioned to warn against it, not recommended."
378
+ },
379
+ "passed": true
380
+ },
381
+ {
382
+ "output": "Call it synchronously at the top level of `<script setup>`, not inside `onMounted` or any `async` callback:\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = await useOrders()\n</script>\n```\n\nA few reasons this is the right spot, and not `onMounted`:\n\n**Nuxt composables need a synchronous injection context.** `useFetch`, `useAsyncData`, and any custom composable that internally calls them (or `useRoute`, `useRuntimeConfig`, etc.) rely on Nuxt's current-instance/`nuxtApp` context being resolvable at the moment the composable is called. Nuxt resolves that context synchronously during setup. Calling the composable inside `onMounted`—or after an `await`—means that context is no longer the setup-time instance, and you get warnings like \"composables must be called synchronously\" or a broken/duplicated fetch.\n\n**\"As soon as the page mounts\" is actually what top-level setup gives you, and better.** The idiomatic framing for a page-level fetch is \"as soon as the page renders\"—the page's setup runs on the server during SSR and again on the client during hydration, giving you data in the same render pass instead of after a client-side mount. If your composable wraps `useFetch`/`useAsyncData`, it already dedupes across server and client and hydrates from the server-fetched payload without a second network request. A fetch inside `onMounted` runs only on the client, always after hydration, so you lose SSR data and add a request waterfall—exactly the thing `useFetch` exists to avoid.\n\nIf you genuinely need something post-interaction (a \"load more\" button, a search box, a refresh after a mutation), that's the one case where you'd call a plain `$fetch` inside an event handler or `onMounted` instead—but that's not what you described here.\n\n**Two things to check in your composable:**\n\n1. **Give it an explicit, stable key** if it wraps `useAsyncData`/`useFetch`, and don't derive the key from anything that changes every render—an unstable key defeats dedup and causes refetch loops.\n2. **Make sure it returns the async data, not just side-effects.** If your composable fires a fetch but doesn't `return` the `useFetch` result (or doesn't `await` it in setup), the page may render before data is ready and you'll be tempted to \"fix\" it with `onMounted`—which reintroduces the original problem. Return `{ data, pending, error, refresh }` and consume it in the template or via `await`.\n\nOne caveat: if your composable does something like reading `window` or a browser-only API, that's a real client-only concern—but the fix is to guard that specific access (or use `import.meta.client`), not to move the whole composable call into `onMounted`.\n\nSo: top level of `<script setup>`, synchronously. If you're seeing a warning or a double-fetch and that's why you were reaching for `onMounted`, paste the composable and the warning—the cause is almost always an unstable key or a composable that isn't returning its async data, both of which are fixable in place.",
383
+ "outputSha256": "2123f02e4f6a1705d3333b6b655f7c49edbf590fd4e5b538547a1dd6085cef97",
384
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
385
+ "deterministic": [
386
+ true
387
+ ],
388
+ "judge": {
389
+ "verdict": "pass",
390
+ "reason": "Answer says call it synchronously at top level of <script setup>, not inside onMounted or async callback (code sample `await useOrders()` in setup). Explains Nuxt resolves current-instance/nuxtApp context synchronously during setup, so calling in onMounted or after await loses it. No fail criterion: onMounted only warned against."
391
+ },
392
+ "passed": true
393
+ },
394
+ {
395
+ "output": "Call it synchronously at the top level of `<script setup>` in the page component — not inside `onMounted`, and not after an `await`.\n\n```vue\n<script setup lang=\"ts\">\nconst orders = await useOrders() // top level, no onMounted wrapper\n</script>\n```\n\nWhy this location, specifically:\n\n- Nuxt composables (`useFetch`, `useAsyncData`, your `useOrders`) rely on Nuxt's injection context, which is resolved **synchronously at call time** during setup. Calling one inside `onMounted`, inside a `.then()`, or after an `await` means that context is already torn down or the component instance is no longer current, so the composable either throws, warns, or silently attaches to the wrong instance.\n- `onMounted` only runs in the browser, so anything you fetch there is skipped during SSR. The user gets no server-rendered content and immediately fires a client-side request after hydration — exactly the double-fetch/flash-of-empty pattern you want to avoid for \"fetch as soon as the page mounts\" data.\n- Top-level `await` in `<script setup>` suspends the component during SSR and resumes it on the client with the payload embedded, so the data is there on first paint with no second request.\n\nSo the \"as soon as the page mounts\" intent is actually best served by top-level *setup*, not by the mount hook.\n\nA few related points for your `useOrders` composable:\n\n- Inside it, use `useFetch` or `useAsyncData` with an **explicit, stable key** (e.g. `'user-orders'`), so it dedupes across server and client and hydrates without refetching.\n- If your composable needs async work beyond what `useFetch` does, still keep the `useFetch`/`useAsyncData` call at the top of the composable body synchronously, and put only the deferred extra logic behind the returned promise/data.\n- Reserve a plain `$fetch` called from an event handler or `onMounted` **only** for genuinely client-only, post-interaction fetches (a \"load more\" button, a search-on-type box) — not for data the page needs on load.\n- If the orders depend on a reactive source (a filter, a route param), pass it to `useFetch`'s `query`/`watch` options rather than re-calling the composable inside a watcher.\n\nOne caveat if there's more than one package in your repo: confirm `nuxt.config.ts` and this `pages/` directory are in the same app/package you're editing, since a monorepo can have both a Nuxt app and a Next.js app and the injection rules above are Nuxt-specific.\n\nAfter wiring this up, verify with `nuxi typecheck` and `nuxi build` — a misplaced composable call typically surfaces as a build warning or an auto-import resolution error rather than a clean failure.",
396
+ "outputSha256": "712144a6e42b058ade68ba940931f95dda3f2431a798f657b28723524cd1c33b",
397
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
398
+ "deterministic": [
399
+ true
400
+ ],
401
+ "judge": {
402
+ "verdict": "pass",
403
+ "reason": "Answer says call it synchronously at top level of <script setup>, not inside onMounted or after await (code sample `const orders = await useOrders()` top level, no onMounted wrapper). Explains Nuxt injection context is resolved synchronously at call time, so deferred calls lose it. No fail criterion: onMounted/await placement only warned against."
404
+ },
405
+ "passed": true
406
+ },
407
+ {
408
+ "output": "Call your composable synchronously at the top level of `<script setup>` in the page — not inside `onMounted`, and not inside any async callback or after an `await`.\n\nSo this:\n\n```ts\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = useOrders()\n</script>\n```\n\n...not this:\n\n```ts\n<script setup lang=\"ts\">\nconst orders = ref(null)\nonMounted(async () => {\n orders.value = await useOrders() // wrong: composable called after setup completes\n})\n</script>\n```\n\nWhy the top level matters: Nuxt composables (`useFetch`, `useAsyncData`, `useRoute`, and your `useOrders` if it wraps any of these) resolve their injection context — the current Nuxt instance, route, and payload — synchronously at call time. Calling one inside `onMounted` or after an `await` means there is no active instance to attach to, so it either throws (\"must be called at the top level of a setup function\") or silently loses SSR payload hydration.\n\nIf your `useOrders` is built on `useFetch`/`useAsyncData`, calling it at setup top level already runs the request during SSR, dedupes it across server and client, and hydrates the result without a second request on the client. That is the \"fetch as soon as the page mounts\" behavior you want, and it happens before/without needing an `onMounted` hook at all. You get `pending` and `error` refs back to drive loading and error UI, and the data is available immediately in a reactive form for the template.\n\nTwo things to make sure of inside your composable:\n\n- Give the underlying `useFetch`/`useAsyncData` a stable, explicit key (e.g. `'user-orders'`, or a computed key if it depends on route params) so server and client renders resolve to the same cached entry.\n- If the composable calls `useRoute()`, `useNuxtApp()`, or similar, those must also be at its top level — same synchronous-context rule applies one level down.\n\nOnly reach for `onMounted` + a plain `$fetch` when the call is genuinely client-only and post-interaction (e.g. a \"load more\" button, a search box). For data the page needs on first render, top-level composable setup is correct.\n\nIf you want, share what `useOrders` wraps internally (raw `useFetch`, a custom `$fetch` call, etc.) and I can check the key/context setup for you.",
409
+ "outputSha256": "90881a5b4d5a0f2740c6097fce6862987b34e00bf7a985f450a8cd4d80960413",
410
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
411
+ "deterministic": [
412
+ true
413
+ ],
414
+ "judge": {
415
+ "verdict": "pass",
416
+ "reason": "Answer states call composable synchronously at top level of <script setup>, not inside onMounted/async callback/after await (shows correct and wrong code). Explains Nuxt composables resolve injection context (instance, route, payload) synchronously at call time, so deferred call loses it. Fail criterion not triggered: onMounted+await shown only as wrong example."
417
+ },
418
+ "passed": true
419
+ },
420
+ {
421
+ "output": "Call it synchronously at the top level of `<script setup>` — not inside `onMounted`, not inside an async callback:\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = await useOrders()\n</script>\n```\n\nThe key point: Nuxt resolves a composable's injection context **synchronously at call time**. `useFetch`/`useAsyncData` (and any custom composable wrapping them) rely on `useNuxtApp()`/`useAsyncData` being invoked in that synchronous setup window. Calling it inside `onMounted` or after an `await` moves it outside that window, so the composable can't attach to the right Nuxt instance/route context — you'll get context-missing errors or it silently won't participate in SSR.\n\nWhy this also gives you \"as soon as the page mounts\" for free:\n\n- With SSR, the fetch runs **on the server during render**, so the data is already resolved by the time the client hydrates — the client doesn't wait for a mount-triggered request at all.\n- On client-side navigation, Nuxt runs the setup (and thus your composable) as part of resolving the page, before it's shown.\n- The payload hydrates across server → client, so there's no duplicate request on hydration.\n\nIf you genuinely need DOM/A-B-C behavior on mount, put only *that* logic in `onMounted`, and let the data call stay at the top level:\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders } = await useOrders() // fetch: setup-time, SSR-friendly\n\nonMounted(() => {\n // DOM-only work here, e.g. measuring, focusing, third-party widget init\n})\n</script>\n```\n\nA few related cautions:\n\n- **Don't wrap it in `onMounted` to \"be safe.\"** That's the specific mistake this pattern exists to avoid, and it forfeits SSR data plus adds a client round trip.\n- **Give it a stable key** if your `useOrders` composable calls `useAsyncData` internally (`useAsyncData('orders', () => $fetch('/api/orders'))`), so it dedupes and hydrates correctly.\n- **Make sure it's not imported from server-only code.** If `useOrders` reaches into `server/api` helpers or secrets, fix that — a composable runs in both the server and client bundle.\n- **Reserve `$fetch` in `onMounted`/an event handler** for genuinely client-only, post-interaction calls (a \"load more\" button, a search-as-you-type). Your case — orders needed when the page appears — is not that.\n\nIf your composable is written to work only with a mounted component (it reads a template ref, etc.), that's a bug in the composable to fix, not a reason to move the call into `onMounted`.",
422
+ "outputSha256": "3ad45d81fd23179953ca0e4cf2941eddceef7747c60a545d85ab8c6248138226",
423
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
424
+ "deterministic": [
425
+ true
426
+ ],
427
+ "judge": {
428
+ "verdict": "pass",
429
+ "reason": "Answer states call it synchronously at top level of <script setup>, not inside onMounted or async callback, and shows code. Explains Nuxt resolves injection context synchronously at call time, so deferred call loses Nuxt instance/route context. Warns against onMounted placement rather than recommending it. Both pass criteria met; no fail criterion holds."
430
+ },
431
+ "passed": true
432
+ },
433
+ {
434
+ "output": "Call it synchronously at the top level of the page's `<script setup>`, right alongside your other composables — not inside `onMounted`, not after an `await`, not inside a callback:\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = useUserOrders()\n</script>\n```\n\nIf you wrote the composable yourself, it should itself call `useFetch`/`useAsyncData` synchronously at its own top level so that everything resolves during setup, before SSR finishes. Something like:\n\n```ts\n// composables/useUserOrders.ts\nexport function useUserOrders() {\n return useFetch('/api/orders', { key: 'user-orders' })\n}\n```\n\nThe reason \"as soon as the page mounts\" doesn't translate to `onMounted` here: Nuxt composables like `useFetch`/`useAsyncData` resolve their injection context (the Nuxt instance) synchronously at call time. Calling one inside `onMounted`, or after an `await`, or inside any async callback, means that context is already gone — you'll either get \"composable called outside of setup\" errors or a version that silently skips SSR and re-fetches on the client, which is exactly the double-fetch/hydration problem `useFetch` exists to avoid.\n\nTwo things worth checking:\n\n- If the data depends on a reactive source (a route param, a filter ref), pass it into your composable and let `useFetch` react to it — don't wrap the call in `watch` or move it into an effect. If you truly need to delay or conditionally run it, use `useAsyncData` with `immediate: false` and call its `execute()` from an event handler, not `onMounted`.\n- Give it a stable `key`. That's what makes the server-rendered payload hydrate into the client without a second request. A key computed from reactive values is fine; a random or missing one is not.\n\nSo: top of `<script setup>`, synchronous, no `await` before it. If you need post-interaction fetching (a \"load more\" button, a search box), that's the opposite case — use a plain `$fetch` inside the handler, and your setup-time composable can stay as-is.",
435
+ "outputSha256": "8e75ba498d985f47f84fd00aabf612d7a7a20b4dd28b6cce257cbfde2eced7da",
436
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
437
+ "deterministic": [
438
+ true
439
+ ],
440
+ "judge": {
441
+ "verdict": "pass",
442
+ "reason": "Answer states call it synchronously at top level of <script setup>, not inside onMounted/after await/callback (code block shows `const { data: orders } = useUserOrders()` at top level). Explains why: Nuxt composables resolve injection context (Nuxt instance) synchronously at call time; deferred call loses it, causing errors or skipped SSR. No fail criterion: onMounted only warned against."
443
+ },
444
+ "passed": true
445
+ },
446
+ {
447
+ "output": "Call the composable synchronously at the top level of `<script setup>` — not inside `onMounted`, and not inside any async callback.\n\n```vue\n<script setup lang=\"ts\">\nconst { orders, pending, error } = useUserOrders()\n</script>\n```\n\nWhy this placement, specifically:\n\n- **Nuxt composables resolve their context at call time, synchronously.** `useFetch`/`useAsyncData` (and composables built on them) need to be invoked during setup so Nuxt can bind them to the current component instance and register them for server-side rendering and payload hydration. Calling one inside `onMounted` or after an `await` runs outside that window — the context is already gone, and you'll get either a runtime error or a fetch that never participates in SSR.\n- **The \"fetch as soon as the page mounts\" requirement is already satisfied by rendering itself.** A top-level `useFetch` runs during the page's server render, so the data is present in the initial HTML. There's no mount-timing gap to close — the data arrives *before* the mount, which is strictly better than fetching on mount. Wrapping it in `onMounted` would actually make it client-only and reintroduce a loading flash on first paint.\n\nIf your composable internally uses `useFetch`/`useAsyncData`, make sure it passes an explicit, stable key (e.g. `'user-orders'`, or something including the user id if orders are per-user). Without a key, Nuxt generates one from the call site, which can diverge between server and client and cause a redundant client-side refetch or a hydration mismatch.\n\nTwo things worth checking while you're in there:\n\n1. **Auth scope.** If orders are per-user, the key must include the user id, and the request should carry the session (which `useFetch` does automatically for same-origin calls). Don't cache one user's orders under a key that another request on the same server could hit.\n2. **Genuinely client-only data.** If any of the orders data depends on something that only exists in the browser (a value from `localStorage`, for instance), the correct move is *not* to move the composable into `onMounted` — it's to split the concern: fetch the SSR-safe portion at top level, and handle the browser-only piece separately. Mixing the two is what usually motivates the `onMounted` mistake in the first place.\n\nOnly reach for a plain `$fetch` inside an event handler or `onMounted` for calls that are truly post-interaction — a \"load more\" button, a search-as-you-type — where there's no server-render equivalent to hydrate from. A page's initial user-orders load is not that case.",
448
+ "outputSha256": "3cddc4b42745a1732187b4bb7d2051d553feff87321b307f21e30804fdda8ced",
449
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
450
+ "deterministic": [
451
+ true
452
+ ],
453
+ "judge": {
454
+ "verdict": "pass",
455
+ "reason": "Answer states to call the composable synchronously at top level of <script setup>, not inside onMounted or async callback (code sample `const { orders, pending, error } = useUserOrders()` at top level). Explains Nuxt composables resolve context at call time synchronously, so onMounted/after-await loses context. No fail criterion: onMounted only warned against."
456
+ },
457
+ "passed": true
458
+ },
459
+ {
460
+ "output": "Call it synchronously at the top level of `<script setup>` in the page — not inside `onMounted`, and not after any `await`. For example:\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = await useOrders()\n</script>\n```\n\nThe key point, and the reason `onMounted` is the wrong place here: Nuxt's composables (including `useFetch`/`useAsyncData` underneath your custom one, and anything needing the Nuxt instance like `useRoute`, `useNuxtApp`, `useCookie`) resolve their injection context **synchronously at call time**. If you invoke `useOrders()` inside an `onMounted` callback or after an `await` in your setup, Nuxt has already unwound that synchronous context, and the composable either throws a \"must be called at the top level\" error or silently loses access to the instance. \"As soon as the page mounts\" is not the condition that matters — \"during the synchronous setup pass\" is.\n\nThis also gets you the behavior you actually want for a page's initial data:\n\n- Your composable's internal `useFetch`/`useAsyncData` runs during SSR, so the orders are fetched server-side and **hydrated** into the client without a second round trip — no post-mount flash of empty state.\n- Give the underlying `useAsyncData` a stable, explicit `key` (e.g. `useAsyncData('user-orders', ...)`), so server and client dedupe to the same payload.\n- If you `await` the composable at the top level of `<script setup>`, Nuxt treats it as a page-level async setup: the page won't render until the data resolves. If you'd rather render a skeleton immediately and let the data stream in, drop the `await` and just use the returned `pending` ref to branch in the template.\n- `onMounted` is the right home for genuinely client-only, post-interaction work — a \"load more\" button, a search-as-you-type call, a browser-API-only fetch — where you deliberately do *not* want a server round trip. Initial page data is not that case.\n\nOne caveat worth stating: this same rule means you should call it in a place that *is* a valid synchronous setup context if you factor it out — a page's `<script setup>`, another composable, a plugin, or route middleware are all fine; an async callback passed to something else is not.",
461
+ "outputSha256": "fbc7cadc9a163aa2026c558a3481b6ea454a21605677c5c5c9d95d2871e4133e",
462
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
463
+ "deterministic": [
464
+ true
465
+ ],
466
+ "judge": {
467
+ "verdict": "pass",
468
+ "reason": "Answer states call it synchronously at top level of <script setup>, not inside onMounted or after await (\"Call it synchronously at the top level... not inside onMounted, and not after any await\"). Explains why: composables resolve injection context synchronously at call time, so deferred call loses Nuxt instance access. No fail criterion: onMounted only warned against."
469
+ },
470
+ "passed": true
471
+ },
472
+ {
473
+ "output": "Call it synchronously at the top level of `<script setup>` — not inside `onMounted`, not inside an async callback, not after an `await`.\n\n```vue\n<script setup lang=\"ts\">\nconst { data: orders, pending, error } = await useOrders()\n</script>\n```\n\nThe reasoning, since this is the part that trips people up:\n\nNuxt composables like `useFetch`, `useAsyncData`, `useRoute`, and a custom `useX` that wraps them depend on Nuxt's injection context (the current instance / Nuxt app) being available at the moment they're called. Nuxt resolves that context **synchronously**, at the point of invocation during setup. If you move the call into `onMounted`, or after an `await`, or inside a `.then()`, that synchronous context is gone and you'll get either `[nuxt] instance unavailable` or a composable that silently fails to hydrate — often surfacing later as duplicate fetches, missing SSR data, or hydration mismatches.\n\nSo \"as soon as the page mounts\" in Nuxt terms means **during setup**, before the component renders. That's actually better than `onMounted` for your case because:\n\n- `useFetch`/`useAsyncData` run on the **server** during SSR and again (reusing the payload, not re-fetching) on the client. The orders are in the HTML on first paint.\n- If you waited for `onMounted` (which only runs client-side), you'd lose SSR entirely for this data, get a loading flash, and pay a round trip after hydration.\n\nA few concrete points for your custom composable:\n\n**Give the fetch a stable key.** If `useOrders` wraps `useFetch`/`useAsyncData`, pass an explicit key so server and client dedupe to the same entry:\n\n```ts\nexport const useOrders = () => {\n return useAsyncData('user-orders', () => $fetch('/api/orders'))\n}\n```\n\nWithout a stable key, Nuxt generates one from call-site characteristics, and mixing it up can cause the client to refetch what the server already fetched.\n\n**Top-level `await` only if the page needs the data to render correctly on the server.** `await useOrders()` blocks rendering until resolved — use that when the content isn't meaningful without orders. If you're fine rendering a shell immediately and streaming, don't await; use `pending`/`error`/`data` from the return value in the template.\n\n**If you need it reactive to a param** (a filter, a route param), pass it as a source rather than calling in a watcher:\n\n```ts\nconst route = useRoute()\nconst { data } = useAsyncData(\n `orders-${route.params.status}`,\n () => $fetch('/api/orders', { query: { status: route.params.status } }),\n { watch: [() => route.params.status] }\n)\n```\n\n**Keep `$fetch`-in-`onMounted` for genuinely client-only, post-interaction work** — a search box firing on keystroke, a \"load more\" click. Fetching the page's primary data on mount is not that case; it's exactly what `useFetch`/`useAsyncData` exist for.\n\nOne thing to check in your composable itself: if it calls `useRoute`, `useRuntimeConfig`, or any other Nuxt composable internally, it inherits the same constraint — so those calls must also be synchronous within the composable's body, not deferred inside a promise it returns.",
474
+ "outputSha256": "f7a54eadb6daba179136c2f4579db3fd1a8fa75e3d586b192202e144e0d63385",
475
+ "promptSha256": "2e0428be001709220e96c52fffc137ff94cfa5112984aa6d041bf6f7cb5a3f90",
476
+ "deterministic": [
477
+ true
478
+ ],
479
+ "judge": {
480
+ "verdict": "pass",
481
+ "reason": "Answer states to call it synchronously at top level of <script setup>, not inside onMounted/async callback/after await (opening line + code). Explains Nuxt resolves injection context synchronously at call time, so deferred calls lose it ('[nuxt] instance unavailable'). No fail criterion: onMounted placement only warned against."
482
+ },
483
+ "passed": true
484
+ }
485
+ ]
486
+ }
487
+ ],
488
+ "verdict": "fail",
489
+ "scope": "bundled",
490
+ "skillDigest": "3ff1ba5b6b957fe007b4df125518564d18a150de40a7537709b6d11a0d7e5f8f",
491
+ "catalogDigest": "139cc7e7c63f9a3fcb3560551b7740c5642c828db2570e2f40728089218cc6d4",
492
+ "judgePromptVersion": "2026-09-25.1",
493
+ "runner": "deepseek",
494
+ "model": "deepseek-chat",
495
+ "runnerPromptVersion": "2026-09-25.1",
496
+ "recordedAt": "2026-09-25T14:58:57.331Z",
497
+ "judge": "deepseek",
498
+ "judgeModel": "deepseek-chat"
499
+ },
500
+ {
501
+ "schemaVersion": "1.0.0",
502
+ "skillId": "nextjs-nuxt/nextjs-nuxt-testing",
503
+ "strictness": "high",
504
+ "trials": 10,
505
+ "triggerAccuracy": {
506
+ "truePositive": 7,
507
+ "falsePositive": 4,
508
+ "positives": 7,
509
+ "negatives": 6
510
+ },
511
+ "evidence": "authored",
512
+ "scenarios": [
513
+ {
514
+ "id": "trigger-positive-1",
515
+ "kind": "trigger-positive",
516
+ "prompt": "This server action creates a new order when called, what's the right way to get test coverage around it?",
517
+ "strictness": "high",
518
+ "trials": 1,
519
+ "passes": 1,
520
+ "passRate": 1,
521
+ "passAtK": 1,
522
+ "grader": "trigger-rank-fork-family",
523
+ "status": "ran",
524
+ "deterministic": true
525
+ },
526
+ {
527
+ "id": "trigger-positive-2",
528
+ "kind": "trigger-positive",
529
+ "prompt": "I've got a composable in my Nuxt app that internally kicks off useAsyncData, what's the best approach for testing it?",
530
+ "strictness": "high",
531
+ "trials": 1,
532
+ "passes": 1,
533
+ "passRate": 1,
534
+ "passAtK": 1,
535
+ "grader": "trigger-rank-fork-family",
536
+ "status": "ran",
537
+ "deterministic": true
538
+ },
539
+ {
540
+ "id": "trigger-positive-3",
541
+ "kind": "trigger-positive",
542
+ "prompt": "This Nuxt component pulls values from useRoute, and I need to render it in a test using @nuxt/test-utils, what's the setup for that?",
543
+ "strictness": "high",
544
+ "trials": 1,
545
+ "passes": 1,
546
+ "passRate": 1,
547
+ "passAtK": 1,
548
+ "grader": "trigger-rank-fork-family",
549
+ "status": "ran",
550
+ "deterministic": true
551
+ },
552
+ {
553
+ "id": "trigger-positive-4",
554
+ "kind": "trigger-positive",
555
+ "prompt": "How do I write a test for what this Next.js App Router server component actually renders?",
556
+ "strictness": "high",
557
+ "trials": 1,
558
+ "passes": 1,
559
+ "passRate": 1,
560
+ "passAtK": 1,
561
+ "grader": "trigger-rank-fork-family",
562
+ "status": "ran",
563
+ "deterministic": true
564
+ },
565
+ {
566
+ "id": "trigger-positive-5",
567
+ "kind": "trigger-positive",
568
+ "prompt": "Write a test for this server/api/orders.ts handler in Nuxt covering the unauthenticated case",
569
+ "strictness": "high",
570
+ "trials": 1,
571
+ "passes": 1,
572
+ "passRate": 1,
573
+ "passAtK": 1,
574
+ "grader": "trigger-rank-fork-family",
575
+ "status": "ran",
576
+ "deterministic": true
577
+ },
578
+ {
579
+ "id": "trigger-positive-6",
580
+ "kind": "trigger-positive",
581
+ "prompt": "There's an authorization check inside this route handler that has zero coverage right now, can you help me write a test for it?",
582
+ "strictness": "high",
583
+ "trials": 1,
584
+ "passes": 1,
585
+ "passRate": 1,
586
+ "passAtK": 1,
587
+ "grader": "trigger-rank-fork-family",
588
+ "status": "ran",
589
+ "deterministic": true
590
+ },
591
+ {
592
+ "id": "trigger-positive-7",
593
+ "kind": "trigger-positive",
594
+ "prompt": "One of my page tests only fails intermittently, and I traced it back to the component calling Date.now() during render, how do I make this deterministic?",
595
+ "strictness": "high",
596
+ "trials": 1,
597
+ "passes": 1,
598
+ "passRate": 1,
599
+ "passAtK": 1,
600
+ "grader": "trigger-rank-fork-family",
601
+ "status": "ran",
602
+ "deterministic": true
603
+ },
604
+ {
605
+ "id": "trigger-negative-1",
606
+ "kind": "trigger-negative",
607
+ "prompt": "Add a new page to this Next.js app router project",
608
+ "strictness": "high",
609
+ "trials": 1,
610
+ "passes": 0,
611
+ "passRate": 0,
612
+ "passAtK": 0,
613
+ "grader": "trigger-rank-fork-family",
614
+ "status": "ran",
615
+ "deterministic": true
616
+ },
617
+ {
618
+ "id": "trigger-negative-2",
619
+ "kind": "trigger-negative",
620
+ "prompt": "Review this Nuxt server/api/orders.ts route for security issues",
621
+ "strictness": "high",
622
+ "trials": 1,
623
+ "passes": 0,
624
+ "passRate": 0,
625
+ "passAtK": 0,
626
+ "grader": "trigger-rank-fork-family",
627
+ "status": "ran",
628
+ "deterministic": true
629
+ },
630
+ {
631
+ "id": "trigger-negative-3",
632
+ "kind": "trigger-negative",
633
+ "prompt": "Fix this Nuxt build error about a composable being called outside setup",
634
+ "strictness": "high",
635
+ "trials": 1,
636
+ "passes": 1,
637
+ "passRate": 1,
638
+ "passAtK": 1,
639
+ "grader": "trigger-rank-fork-family",
640
+ "status": "ran",
641
+ "deterministic": true
642
+ },
643
+ {
644
+ "id": "trigger-negative-4",
645
+ "kind": "trigger-negative",
646
+ "prompt": "Migrate this Nuxt 2 asyncData call to useAsyncData",
647
+ "strictness": "high",
648
+ "trials": 1,
649
+ "passes": 0,
650
+ "passRate": 0,
651
+ "passAtK": 0,
652
+ "grader": "trigger-rank-fork-family",
653
+ "status": "ran",
654
+ "deterministic": true
655
+ },
656
+ {
657
+ "id": "trigger-negative-5",
658
+ "kind": "trigger-negative",
659
+ "prompt": "Write a Jest test for a plain React hook with no routing or SSR concern",
660
+ "strictness": "high",
661
+ "trials": 1,
662
+ "passes": 0,
663
+ "passRate": 0,
664
+ "passAtK": 0,
665
+ "grader": "trigger-rank-fork-family",
666
+ "status": "ran",
667
+ "deterministic": true
668
+ },
669
+ {
670
+ "id": "trigger-negative-6",
671
+ "kind": "trigger-negative",
672
+ "prompt": "Fix this generic ESLint no-floating-promises failure in a plain Node.js script",
673
+ "strictness": "high",
674
+ "trials": 1,
675
+ "passes": 1,
676
+ "passRate": 1,
677
+ "passAtK": 1,
678
+ "grader": "trigger-rank-fork-family",
679
+ "status": "ran",
680
+ "deterministic": true
681
+ },
682
+ {
683
+ "id": "mock-network-not-hook",
684
+ "kind": "behavior",
685
+ "prompt": "I'm writing a test for a Nuxt page that calls useFetch to load a list of orders. Should I mock useFetch directly to return canned data, or something else?",
686
+ "strictness": "high",
687
+ "trials": 10,
688
+ "passes": 10,
689
+ "passRate": 1,
690
+ "passAtK": 1,
691
+ "grader": "regex+judge",
692
+ "status": "ran",
693
+ "trialRecords": [
694
+ {
695
+ "output": "Don't mock `useFetch` directly — mock the network layer beneath it instead.\n\nMocking `useFetch` to return canned data means your test only exercises the mock. Your page's real request handling — the URL it builds, how it reacts to the `pending`/`error` states, what it does with a 4xx or a malformed payload — never actually runs. You'd get a green test that proves nothing about the code path a user will hit.\n\nWhat to do instead, by what you're testing:\n\n**If the point is the component's fetch/loading/error handling** — mock HTTP at the boundary with MSW (or whatever network mock layer the project already uses). Let `useFetch` actually run against that mocked server, so the real fetch logic, error branch, and loading state all execute. Mount the page with `@nuxt/test-utils`' `mountSuspended` so Nuxt's injection context (route, payload, `$fetch`) is present — a plain Vue Test Utils `mount()` won't have that context and will fail or silently diverge.\n\n**If the composable/page wraps Nuxt context more deeply** and you genuinely can't get a real request through, the next-best lever is mocking `$fetch` at a level below `useFetch`, not `useFetch` itself. That still lets `useFetch`'s own state machine run.\n\n**If you only care about how the list renders given already-resolved data** — that's a presentational test, not a fetch test. Extract or accept the orders as a prop and test the presentational piece directly; don't dress it up as a `useFetch` test.\n\nA few things to nail down regardless of which path you take:\n\n- Cover the failure path, not just the happy path. The orders page should have at least one assertion for the error state (and, if it renders one, the empty-list state). \"Canned success data\" as the only case is exactly what the `useFetch` mock tends to produce.\n- Make it deterministic. Anything rendering `Date.now()`, a relative timestamp, or locale-dependent formatting needs an injected/frozen clock (`vi.setSystemTime` or equivalent) before it reaches an assertion — otherwise you'll get a flaky test that looks like a Nuxt context problem but isn't.\n- Build `useRoute()`/params mocks from a realistic fixture shape, not a partial object that happens to satisfy today's assertions.\n\nVerify by running the project's test command: the test should fail against the pre-implementation code (or, for a bug-fix, against the buggy code), and pass once the page behaves correctly — that's the check that it's exercising real behavior rather than a mock.",
696
+ "outputSha256": "bc33469a75160059e29b2656346580bc36dc4abe41a8173e6f76d1d6dea997c9",
697
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
698
+ "deterministic": [
699
+ true
700
+ ],
701
+ "judge": {
702
+ "verdict": "pass",
703
+ "reason": "Answer says \"Don't mock useFetch directly — mock the network layer beneath it instead\" and recommends MSW at the HTTP boundary letting useFetch run. It explains mocking useFetch \"means your test only exercises the mock\" and real request/loading/error handling \"never actually runs.\" Both pass criteria met; no fail criterion."
704
+ },
705
+ "passed": true
706
+ },
707
+ {
708
+ "output": "Mock at the network boundary instead of mocking `useFetch` itself.\n\n**Why not mock `useFetch` directly:**\nReplacing `useFetch` with a stub that returns canned data means your test no longer exercises the code you actually care about — the fetch call, the reactive data/error/loading state wiring, the request URL/params being built correctly, and how the component renders those states. You end up testing your own mock, not the page.\n\n**What to do instead:**\n- Use MSW (`msw`) or your project's existing network mock layer to intercept the outgoing HTTP request and return a realistic response. `useFetch` then runs for real on top of that, so the component's request/response handling, loading state, and error path all stay covered.\n- If you specifically need to control the SSR/CSR fetch behavior of the Nuxt runtime, use `@nuxt/test-utils`'s Nuxt test context (via `mountSuspended`/`renderSuspended` for components, or its server-rendering helpers for pages) so `useFetch` has real Nuxt context — combined with a network-level mock, not a hole in `useFetch`.\n- Only if the test's *entire point* is some other concern (e.g. pure layout given already-resolved data) should you bypass the fetch at all — and even then prefer a network mock with a fixed fixture response over stubbing the hook.\n\n**Cover at least these cases with the network mock:**\n- Success path renders the orders list.\n- Error response (non-2xx or network failure) renders the error state, not a crash.\n- The request hits the URL/params you expect (assert on the intercepted request).\n\n**Also make it deterministic:** if the page renders anything time- or locale-dependent (formatted order dates, relative timestamps), freeze the clock (`vi.setSystemTime` or a shared fake-timer setup) and avoid asserting on unfrozen `Date.now()`/locale output — that's a common source of flaky SSR/hydration tests, and skipping such a test just hides a real hydration mismatch rather than fixing it.\n\nQuick rule: mock the network call the page makes, run `useFetch` for real.",
709
+ "outputSha256": "361b65fd35b0199817b638cb3275547999c1277049d08559a6b3fc7a0d026d2b",
710
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
711
+ "deterministic": [
712
+ true
713
+ ],
714
+ "judge": {
715
+ "verdict": "pass",
716
+ "reason": "Answer recommends MSW/network mock instead of stubbing useFetch ('Use MSW (msw) or your project's existing network mock layer to intercept the outgoing HTTP request'), and explains mocking useFetch 'means your test no longer exercises the code you actually care about... You end up testing your own mock, not the page.' No fail criterion: it warns against stubbing the hook, not recommending it."
717
+ },
718
+ "passed": true
719
+ },
720
+ {
721
+ "output": "Do not mock `useFetch` directly to return canned data. Mock at the network boundary instead — use MSW (or your project's existing network mock layer) to intercept the actual HTTP request that `useFetch` makes. Mocking the hook itself means your test proves nothing about the component's real loading, error, and response-handling behavior; it only proves your stub returned what you told it to.\n\nConcretely, for a Nuxt page that calls `useFetch` to load orders:\n\n1. **Mount with the Nuxt test context.** Use `@nuxt/test-utils`'s `mountSuspended` (or `renderSuspended`) rather than plain Vue Test Utils `mount()`, because `useFetch` needs Nuxt's injection/runtime context. A plain mount will fail or silently lack context.\n\n2. **Mock the network, not the hook.** Register an MSW handler for the orders endpoint that returns your canned order list. Then let `useFetch` actually run its real code path, parse the response, and populate the component. You get coverage of the request wiring, the loading-to-resolved transition, and the rendering of real fetched data.\n\n3. **Write more than the happy path.** Add handlers for a 500 and for an empty list, and assert the component's error state and empty state respectively. That is the behavior you actually care about and the reason not to stub `useFetch` — a stubbed hook lets you fake any state trivially but exercises none of the component's handling of it.\n\n4. **Keep it deterministic.** If anything in the page renders time or locale-dependent output, freeze the clock (`vi.setSystemTime` or equivalent) so the SSR/hydration-sensitive assertions don't flake.\n\n5. **Verify.** Run the project's test command, and confirm the test fails if you revert the component's fetch/error handling, then passes with it.\n\nThe short rule: when the point of the test is how the component handles a real request and response, mock the network layer beneath `useFetch`, never `useFetch` itself. Only reach for a direct hook stub in the rare case where you are testing something entirely unrelated to fetching and just need the page to render — and even then, be explicit that you are not testing the fetch behavior at all.",
722
+ "outputSha256": "f3a90d8e4d45008b54614335eb7ecde1a2cf328a621cd7bdbfdf22e08746d20f",
723
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
724
+ "deterministic": [
725
+ true
726
+ ],
727
+ "judge": {
728
+ "verdict": "pass",
729
+ "reason": "Answer explicitly says 'Do not mock useFetch directly' and recommends MSW at the network boundary ('intercept the actual HTTP request that useFetch makes'). It explains mocking the hook 'proves nothing about the component's real loading, error, and response-handling behavior; it only proves your stub returned what you told it to.' Both pass criteria met; no fail criterion."
730
+ },
731
+ "passed": true
732
+ },
733
+ {
734
+ "output": "Don't mock `useFetch` directly. Mock the network boundary beneath it (MSW or your project's existing network mock layer) and let the component call the real `useFetch`.\n\nHere's the reasoning:\n\n- **What you're trying to test is the component's request/response handling** — the loading state, the error branch, the shape it expects from the API, what it renders on a 4xx/5xx or a slow response. If you stub `useFetch` to return canned data, you've replaced exactly the code path you set out to exercise. The test then only proves that your component can render a plain object you handed it, which is close to a tautology.\n- **Mocking at the network layer keeps the real `useFetch` in the loop.** MSW intercepts the actual HTTP call, so Nuxt's real `useFetch` runs: its `$fetch` integration, its reactive `data`/`error`/`pending` refs, its serialization/deserialization, and its SSR/CSR transfer behavior. That's where real bugs live (wrong URL, wrong method, unhandled non-2xx, error object shape mismatches).\n- **It also lets you test the paths `useFetch` mocking tends to skip entirely**: a 500 response, a network failure, and a pending state you can control. With direct hook mocking, those are easy to omit because stubbing them is more work than stubbing the happy path.\n\nConcretely for a Nuxt page test:\n\n1. Use `@nuxt/test-utils`'s `mountSuspended` (or `renderSuspended` for an SSR render) so `useFetch` has the Nuxt runtime context it needs — plain Vue Test Utils `mount()` won't provide it.\n2. Register MSW handlers for the orders endpoint in your test setup (follow `rules/testing.mdc` for layout and the network mock layer).\n3. Assert on the rendering outcomes across states: the list after a successful response, an error UI after a 500, and a loading/skeleton state if the component has one.\n4. For the success assertion, build the order fixture with a factory rather than a hand-built partial, so the component's field access reflects a realistic payload.\n\nThe one case where mocking `useFetch` is legitimate is a purely presentational child component that receives already-fetched data as props and never calls `useFetch` itself — but then you're not testing `useFetch` at all, so there's nothing to mock.\n\nTwo things to watch:\n\n- If a test around this is flaky due to `Date.now()` or locale-dependent formatting in the rendered order dates, freeze the clock (e.g. `vi.setSystemTime`) rather than mocking the fetch layer or skipping the test — that flakiness is the same nondeterminism that causes real SSR/hydration mismatches in production.\n- Don't fall back to `mount()` instead of `mountSuspended` just because Nuxt context errors are annoying to set up; the composable genuinely needs that context, and a plain mount will either fail or silently test something other than your page.",
735
+ "outputSha256": "d907716d77dc5f4a21b771c0bb59c2ab2d77810feae631fc5d1ae27a6d8588f9",
736
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
737
+ "deterministic": [
738
+ true
739
+ ],
740
+ "judge": {
741
+ "verdict": "pass",
742
+ "reason": "Answer says \"Don't mock useFetch directly. Mock the network boundary beneath it (MSW...)\" (criterion 1) and explains stubbing useFetch \"replaced exactly the code path you set out to exercise... only proves that your component can render a plain object you handed it\" (criterion 2). No fail criterion: it warns against useFetch mocking, not recommends it."
743
+ },
744
+ "passed": true
745
+ },
746
+ {
747
+ "output": "Do not mock `useFetch` directly. Mock the network layer beneath it (MSW or whatever network-mock layer the project uses) and let the real `useFetch` run against it.\n\nWhy: if you stub `useFetch` to just return `{ data: orders, pending: false, error: null }`, you have tested nothing about the component's actual behavior. You've asserted that the component renders a list you handed it. You have not exercised the real code path where `useFetch` builds the request, resolves the reactive refs, transitions `pending → false`, or — critically — handles the error and empty states. The whole reason to test a page that fetches is to prove it behaves correctly across loading, success, error, and empty responses, and mocking the hook collapses all four into one canned success.\n\nWith a network-boundary mock:\n\n- Your MSW handler intercepts the request the real `useFetch` makes and returns the fixture. The component goes through its genuine loading state, then renders the resolved data. You can assert on the loading state before the response resolves.\n- To test the error path, have the handler return a 500 (or a network error) and assert the component shows its error UI — this is the case a direct hook mock tempts you to skip, and it's the one most likely to regress.\n- To test the empty state, return `[]` and assert the empty-state branch renders.\n\nKeep the handler shape realistic (a proper `{ data: [...] }` envelope matching your real API, pagination fields if you have them) rather than a minimal object that only satisfies today's assertions.\n\nOne caveat: regardless of how you mock, this component needs Nuxt's test context because it calls `useFetch`, which reads Nuxt runtime injection. So mount it with `@nuxt/test-utils`'s `mountSuspended`, not plain Vue Test Utils `mount()` — a plain mount has no Nuxt context and `useFetch` will fail or misbehave. Pair `mountSuspended` with the network-boundary mock and you get both the real composable behavior and the real request/response handling.\n\nSo: `mountSuspended` + MSW handler on the real endpoint, real `useFetch`, and assertions covering loading, success, error, and empty. Freeze time or inject a clock if the rendered output depends on `Date.now()` or locale, or that will be your flake.",
748
+ "outputSha256": "f69c8ddc02a87211714ffd8bba4d28e76d9f123f709a3a5d7a293b6892a66473",
749
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
750
+ "deterministic": [
751
+ true
752
+ ],
753
+ "judge": {
754
+ "verdict": "pass",
755
+ "reason": "Answer says 'Do not mock useFetch directly. Mock the network layer beneath it (MSW...)' (criterion 1) and explains stubbing useFetch 'you have tested nothing about the component's actual behavior' and skips real request/pending/error/empty handling (criterion 2). No fail criterion: it warns against hook mocking, not recommends it."
756
+ },
757
+ "passed": true
758
+ },
759
+ {
760
+ "output": "Don't mock `useFetch` directly. Mock at the network boundary (MSW or your project's existing network-mock layer) so the page's real `useFetch` request/response handling, loading state, and error path all actually run.\n\nMocking `useFetch` itself just tests the mock: your assertions would prove the component renders whatever object you handed it, not that it correctly drives a request, handles a pending state, or surfaces a fetch failure. You'd get a green test that passes even if the URL were wrong, the response shape mismatched, or error handling were broken.\n\nConcretely for a Nuxt page loading orders:\n\n1. Mount with `mountSuspended` (from `@nuxt/test-utils`) if the page/component relies on any Nuxt runtime context — which a page using `useFetch` does. Plain Vue Test Utils `mount()` has no Nuxt injection context and will fail or behave wrongly here.\n2. Intercept the HTTP request the page makes (e.g. MSW handler for `GET /api/orders`) and return a fixture list built from a factory with a realistic shape.\n3. Assert on the rendered output after the request resolves — and add a case for the error path (handler returns 500 or network error) so you're testing `useFetch`'s error handling, not an assumed success.\n4. If the page renders anything time- or locale-dependent, freeze the clock so it stays deterministic.\n\nSo: keep the real `useFetch`, mock the `$fetch`/HTTP layer beneath it.",
761
+ "outputSha256": "a2d9fb9cdcec729f39a7253c84c42da3e1bcdb0e7b22b18741f87bd09499b057",
762
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
763
+ "deterministic": [
764
+ true
765
+ ],
766
+ "judge": {
767
+ "verdict": "pass",
768
+ "reason": "Answer says \"Don't mock useFetch directly. Mock at the network boundary (MSW...)\" (crit 1) and explains mocking useFetch \"just tests the mock\" — assertions prove rendering of handed object, not real request/pending/error handling (crit 2). No fail criterion: it warns against vi.mock, doesn't recommend it."
769
+ },
770
+ "passed": true
771
+ },
772
+ {
773
+ "output": "Mock at the network boundary, not `useFetch` itself. Stub the HTTP layer (e.g. MSW or the project's existing network mock) to return the canned orders, and let the real `useFetch` in your page run against it.\n\nWhy this matters:\n\n- If you replace `useFetch` with a stub that returns `{ data: orders }`, you are asserting on your mock, not on the page's actual request handling. Every behavior that makes the test worth writing — the loading branch while the request is in flight, the error branch when the request fails, how the page reads `data` vs `error`, any transformation it does on the response — never executes. You can break all of that and the test stays green.\n- A network-boundary mock exercises the real `useFetch` → `$fetch` → fetch path, so the page's own fetch/error/loading logic is what you are testing.\n\nConcretely, for a Nuxt page:\n\n- `useFetch`/`useAsyncData` need Nuxt's runtime context, so mount the page with `@nuxt/test-utils`'s `mountSuspended` (or `renderSuspended`) rather than a bare Vue Test Utils `mount()`. A plain mount has no Nuxt injection context and the composable will not resolve.\n- Have the mock return the orders at the same URL/shape the page actually requests, then assert on what the page renders: a real order in the list, the loading state before the response resolves, the error/empty state when the mock returns a failure or empty array.\n- Build the order fixtures with a factory so the shape matches the real API response, not a partial object that only satisfies today's assertions.\n\nThe one case where calling a hook out of the picture is fine: a pure unit test of a helper that happens to take already-fetched data as an argument. But for \"this page calls `useFetch` to load orders,\" the behavior under test is the fetch handling, so mock the network beneath it.\n\nAlso freeze time for anything rendering dates (`vi.setSystemTime` or inject a clock) — order lists typically render timestamps, and that nondeterminism is the usual cause of the flakiness you would otherwise be tempted to skip around.",
774
+ "outputSha256": "58fe1afe0af7353624e61c752acf494f0e995c2c4b1dcb0f4e7012b449a0a36c",
775
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
776
+ "deterministic": [
777
+ true
778
+ ],
779
+ "judge": {
780
+ "verdict": "pass",
781
+ "reason": "Answer says 'Mock at the network boundary, not useFetch itself' and 'Stub the HTTP layer (e.g. MSW...)'. It explains replacing useFetch with a stub means 'you are asserting on your mock, not on the page's actual request handling', skipping loading/error/data branches. No fail criterion: it warns against mocking useFetch, not recommending it."
782
+ },
783
+ "passed": true
784
+ },
785
+ {
786
+ "output": "Don't mock `useFetch` directly. Mock the network boundary underneath it instead — MSW (or whatever network-mock layer the project already uses) intercepting the request `useFetch` would make.\n\nReason: mocking `useFetch` itself replaces the exact code you're trying to test. The component's real behavior — building the request, handling the loading state, handling an error response, handling an empty vs. populated result — lives in what it does with `useFetch`'s real contract, not in a hand-rolled `{ data: [...] }` object that happens to satisfy today's `v-if`. Mocking the hook means your test passes even if the URL, query params, headers, or error handling are wrong.\n\nPractically:\n\n- Intercept the endpoint the page actually hits (e.g. `GET /api/orders`) with MSW and return a realistic fixture shaped like the real API response. That exercises `useFetch`'s actual serialization, the page's `pending`/`error`/`data` branches, and the composable wiring end to end.\n- If you need to assert the page sent the right request (query params, headers), assert against the intercepted request inside the MSW handler or capture it — that's the thing worth pinning, and it's invisible if you stub the hook.\n- Keep the fixture realistic (a factory), not a minimal `{ data: [] }`-ish shape, so the test doesn't quietly depend on fields the real API won't always return.\n\nOne caveat: if the page's component itself needs Nuxt's injection context at all (auto-imports, `useRoute`, `useFetch`), mount it with `@nuxt/test-utils`' `mountSuspended`/`renderSuspended`, not plain Vue Test Utils — a Nuxt-context component mounted without that context will fail or behave differently regardless of how you mock the network. Network-boundary mocking and the right mount harness are separate decisions; you need both.\n\nSo: `mountSuspended` for the Nuxt context, MSW for the request. Not a stubbed `useFetch` return value.",
787
+ "outputSha256": "0a44729c32384425040cc89e857c69b1dcdb487c1da42a8fd9474ec824818add",
788
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
789
+ "deterministic": [
790
+ true
791
+ ],
792
+ "judge": {
793
+ "verdict": "pass",
794
+ "reason": "Answer says \"Don't mock useFetch directly. Mock the network boundary underneath it instead — MSW\" (criterion 1, concrete: intercept GET /api/orders with MSW). Criterion 2: \"mocking useFetch itself replaces the exact code you're trying to test... test passes even if the URL, query params, headers, or error handling are wrong.\" No fail criterion: it warns against stubbing the hook, not recommending it."
795
+ },
796
+ "passed": true
797
+ },
798
+ {
799
+ "output": "Don't mock `useFetch` directly. Mock the network boundary underneath it instead — with MSW or your project's existing network-mock layer.\n\nHere's why: if you stub `useFetch` to just return `{ data: cannedOrders }`, your test only proves that the component can render a `data` value you handed it. It never exercises the component's real handling of the request lifecycle — the loading state, the error branch, the actual URL/params the component builds, or the shape parsing it does on the response. You're testing your mock, not the page.\n\nIf instead you intercept the HTTP request the page actually makes, the real `useFetch` code path runs end to end: it fires the request, your mock layer responds, and the component's own request/response handling gets exercised. That's what you want to verify.\n\nA few practical notes specific to the Nuxt case:\n\n- Use `mountSuspended` (or `renderSuspended`) from `@nuxt/test-utils` for this page, since a component calling `useFetch` needs Nuxt's runtime injection context. Plain Vue Test Utils `mount()` won't have it, and the \"fix\" of switching to plain mount is a red flag, not a solution.\n- In your MSW handler, register the exact endpoint `useFetch` hits (including any base URL / query params / `server: false` behavior your composable uses), and return a realistic payload built from a fixture/factory rather than a partial object hand-shaped to satisfy today's assertions.\n- Assert on the states the page actually owns: initial loading, success render of the orders list, and the error branch when the handler returns a 4xx/5xx. The error path is the one people forget, and it's exactly the branch a direct-hook mock silently skips.\n\nThe only time a direct `useFetch` mock is defensible is when the composable is a thin wrapper and the test's stated purpose is purely something downstream of the fetch result (e.g., a pure formatting function) — but for a page loading and rendering a list of orders, the fetch/response handling is part of what you're testing, so go with the network-boundary mock.\n\nOne more thing for determinism: if the page renders any timestamps on those orders via `Date.now()` or does locale-dependent formatting, freeze the clock (`vi.setSystemTime` or inject a clock) and fix the locale, or the test will be flaky for reasons unrelated to the mocking choice.",
800
+ "outputSha256": "418d8182af320f061fcfd95e3a72e14417ee4eeed245c26f6cf22ba3fe4f1ad1",
801
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
802
+ "deterministic": [
803
+ true
804
+ ],
805
+ "judge": {
806
+ "verdict": "pass",
807
+ "reason": "Answer says \"Don't mock useFetch directly. Mock the network boundary underneath it instead — with MSW\" (criterion 1). It explains stubbing useFetch \"only proves that the component can render a data value you handed it... You're testing your mock, not the page,\" skipping loading/error/URL handling (criterion 2). No fail criterion: direct mock only mentioned to warn against."
808
+ },
809
+ "passed": true
810
+ },
811
+ {
812
+ "output": "Do not mock `useFetch` directly. Mock the network boundary beneath it instead — intercept the actual HTTP request with MSW (or whatever network mock layer the project already uses) and let the real `useFetch` run against that.\n\nHere's why it matters for your case specifically:\n\n`useFetch` in a Nuxt page is doing real work you'd be silently skipping over if you stub it out — constructing the request URL, serializing query params, picking up the SSR-aware behavior, populating `pending`/`error`/`status` refs, and integrating with Nuxt's payload hydration. If you replace the whole hook with `{ data: ref(cannedOrders), pending: ref(false), error: ref(null) }`, your test only proves that your page renders an array of orders. It proves nothing about whether the page's loading state, error branch, or empty-list handling ever actually works, because you've hardcoded those states away.\n\nSo concretely:\n\n1. Render the page with `mountSuspended` (or `renderSuspended`) from `@nuxt/test-utils` — the page uses `useFetch`, which needs Nuxt's runtime context, so a plain Vue Test Utils `mount()` won't have it.\n\n2. Set up an MSW handler for the endpoint the page fetches from, e.g. `http.get('/api/orders', () => HttpResponse.json(orders))`, and return a realistic fixture.\n\n3. Let `useFetch` actually run against that handler. Then assert on the rendered list once the request resolves.\n\n4. Test the branches the mock-everything approach would have hidden: return a 500 and assert the error state renders, return `[]` and assert the empty state, and if you want to catch the loading branch, delay the handler response.\n\nTwo practical notes:\n\n- If the page also reads route params or `searchParams` (e.g. `useRoute()` for a filter), build that with a realistic shape from a factory/fixture, not a hand-built partial object that just satisfies today's assertions.\n- Freeze time (`vi.setSystemTime`) if the page renders any date formatting from the orders — order lists very commonly do.\n\nThe one legitimate exception is a unit test whose explicit subject *is* the composable's own logic — e.g. you extracted a `useOrders()` wrapper and want to test its caching/dedup behavior in isolation. Even then, mock the network beneath `$fetch`, not `useFetch` itself, so you're still exercising the real code path.",
813
+ "outputSha256": "3dcd0918d63312b8276b655d2804933a2fbe6690ad5d7e0b634f17d8af2f069f",
814
+ "promptSha256": "b693996969582166e084166ea7af715ec935f3f124dabe51ffbca6b36d65a642",
815
+ "deterministic": [
816
+ true
817
+ ],
818
+ "judge": {
819
+ "verdict": "pass",
820
+ "reason": "Answer says 'Do not mock useFetch directly. Mock the network boundary beneath it instead — intercept the actual HTTP request with MSW' (criterion 1), and explains stubbing the hook 'only proves that your page renders an array of orders... proves nothing about whether the page's loading state, error branch, or empty-list handling ever actually works' (criterion 2). No fail criterion: it warns against mocking useFetch, not recommending it."
821
+ },
822
+ "passed": true
823
+ }
824
+ ]
825
+ },
826
+ {
827
+ "id": "server-action-handler-test-coverage",
828
+ "kind": "behavior",
829
+ "prompt": "I wrote a Next.js server action that creates an order and only has a happy-path test so far. What else should the test cover?",
830
+ "strictness": "high",
831
+ "trials": 10,
832
+ "passes": 10,
833
+ "passRate": 1,
834
+ "passAtK": 1,
835
+ "grader": "judge",
836
+ "status": "ran",
837
+ "trialRecords": [
838
+ {
839
+ "output": "Your happy-path test proves the order gets created when everything lines up. A Server Action is a directly reachable endpoint, so the gaps that actually matter are the ones an attacker or a flaky client can hit. Add these on top of the happy path:\n\n**Auth and authorization**\n- No session / unauthenticated call is rejected before any write happens.\n- A logged-in user acting on someone else's resource (e.g. `userId` in the args not matching the session) is rejected — this is the classic IDOR on a server action, since the client can pass any argument it wants.\n- Assert the rejection path performed no DB write (spy on the downstream client and assert zero calls).\n\n**Validation / input**\n- Missing required fields, wrong types, empty strings, and out-of-range or negative quantities.\n- Malformed payloads that a naive client would never send but a crafted request would (extra/unknown fields, prototype-pollution-ish keys if you spread input).\n- If you use a schema (Zod etc.), test that the schema's failure path returns a structured error rather than throwing an unhandled exception.\n\n**Business-rule failures**\n- Out-of-stock / insufficient inventory.\n- Price or total mismatch (client-submitted price vs. server-recomputed price) — the server must not trust a client-supplied price.\n- Duplicate/idempotency: submitting the same order twice (double-click, retry) doesn't create two orders.\n- Downstream failure: the payment or inventory call rejects/times out — assert the handler surfaces an error and leaves no partial order committed (rollback/transaction behavior).\n\n**Error contract**\n- What the action returns/throws on each failure — a stable error shape the client can distinguish (validation vs. auth vs. conflict), not a raw exception leak.\n\n**Test mechanics for these**\n- Mock one layer *below* the action — the DB client and any external API client — and call the real exported action on top, so the auth and validation logic you're testing actually runs. Don't stub out the action's own internals.\n- Build args from a fixture/factory with realistic shape rather than a hand-built partial that happens to satisfy today's asserts.\n- Freeze time if `createdAt`/totals/locale formatting enters an assertion.\n\nShape-wise, each test should assert the failure didn't mutate state, not just that it returned an error:\n\n```\nconst db = vi.mocked(orderDb.create)\nawait expect(createOrder({ session: null, items })).rejects.toMatchObject({ code: 'UNAUTHENTICATED' })\nexpect(db).not.toHaveBeenCalled()\n```\n\nOne caution on the happy path you already have: since a server action is just a function you call, it's easy for the happy-path test to pass while the auth check is never exercised. The unauthenticated and wrong-user cases above are the ones most likely to be missing, so I'd add those first, then validation, then the idempotency/rollback cases.",
840
+ "outputSha256": "28f9c910641bf08a76d37e4293f03be4f6ed12cca2f88730a68e9ca2ed7f5ef7",
841
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
842
+ "deterministic": [],
843
+ "judge": {
844
+ "verdict": "pass",
845
+ "reason": "Answer names unauthenticated/wrong-user auth failure tests (\"No session / unauthenticated call is rejected\", IDOR case), input-validation failures (missing fields, wrong types, negative quantities, malformed payloads), and says to mock the DB/external client below the action and call the real exported action (\"call the real exported action on top\"), with a code example. No fail criterion present."
846
+ },
847
+ "passed": true
848
+ },
849
+ {
850
+ "output": "For a Server Action that creates an order, the happy-path test is the least valuable one — the action is a directly reachable endpoint, and its real risk lives in the paths your single test doesn't touch. Add tests for:\n\n**Auth / authorization**\n- Unauthenticated caller is rejected (no session, expired session).\n- An authenticated but unauthorized caller (wrong role, or a user attempting to create an order for another account/tenant) is rejected.\n- Assert the action returns/throws the expected error shape, not just that it \"didn't succeed.\"\n\n**Input validation**\n- Missing required fields, wrong types, empty strings, negative/zero quantity, non-integer quantity.\n- Malformed IDs (order ID, product ID) that don't exist or aren't parseable.\n- Values that look valid but are out of range (quantity above a stock limit, price mismatch with the catalog).\n\n**Business-logic failure paths**\n- Product out of stock / discontinued.\n- Downstream dependency fails (DB write fails, payment client throws, external API times out) — mock the dependency one layer down and assert the action surfaces a proper error and does **not** leave a partial order behind.\n- Duplicate submission / idempotency, if the action can be retried.\n\n**Success-path assertions you probably under-tested**\n- The action actually persisted the row (assert against the mocked DB client's write call, not just the return value).\n- A side effect fired (confirmation email, inventory decrement) and fired exactly once.\n- The returned object matches what the client expects to consume.\n\n**Determinism**\n- Freeze or inject the clock if the action stamps `createdAt` or derives an order number from time — otherwise you'll get a flaky test the moment the assertion touches a real timestamp.\n\nTwo things to keep in mind while writing these:\n\n1. **Call the action directly.** Don't route these through a rendered component — construct the args/session yourself and call the exported handler. That's what proves the handler's own auth and validation run.\n2. **Mock at the network/dependency boundary, not the action's interior.** Stub the DB or payment client one layer down and let the real handler logic execute on top of it, so the test exercises the validation and auth code rather than a hand-stubbed return value.\n\nAlso worth a quick check: if any of your happy-path assertions depend on `Date.now()` or a locale-dependent value, fix that nondeterminism now rather than after it goes flaky. And if the action can be reached without the client-side validation your UI applies, the handler's own validation test isn't optional regardless of what the UI already covers.",
851
+ "outputSha256": "f8bce0e8522309f70d1407817ed75ebd02c95c8023d2c1ef8b539485e4f57d6f",
852
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
853
+ "deterministic": [],
854
+ "judge": {
855
+ "verdict": "pass",
856
+ "reason": "Answer names auth failure paths (unauthenticated/expired session, unauthorized wrong role/tenant) and input-validation failures (missing fields, wrong types, negative quantity, malformed IDs). It explicitly says 'Call the action directly... construct the args/session yourself and call the exported handler' and 'Mock at the network/dependency boundary... Stub the DB or payment client one layer down.' No fail criterion holds."
857
+ },
858
+ "passed": true
859
+ },
860
+ {
861
+ "output": "At minimum, add these cases around your server action — the ones the skill's workflow calls out for a route/action handler:\n\n**Auth / authorization**\n- No session / unauthenticated caller → rejected, and the order is not created.\n- Authenticated but not allowed (wrong tenant, wrong role, acting on someone else's cart/items) → rejected, no order created.\n\n**Input validation**\n- Missing required fields (empty / undefined / null).\n- Malformed values: wrong types, negative or zero quantity, negative price, bad SKU/ID format, oversized strings.\n- Referenced entities that don't exist or don't belong to the caller (unknown product, someone else's item).\n- Boundary values: zero items, max allowed items, quantity at the min/max limit.\n\n**Business-rule failures**\n- Out-of-stock / insufficient inventory.\n- Duplicate submission (idempotency key reused, double-submit) → doesn't create a second order.\n- Payment/authorization failure from the downstream client.\n\n**Downstream dependency failures**\n- DB client throws → the action surfaces an error and does not leave a partial order behind.\n- External API returns 5xx/timeout → handled, not swallowed into a false success.\n- Partial write: the order insert succeeds but a later step (e.g. inventory decrement, payment capture) fails → verify rollback or whatever transactional guarantee you claim.\n\n**Failure-path side effects**\n- On every rejection above, assert the negative: no order row created, no charge made, no email/event emitted. Happy-path tests never catch a validation early-return that still persisted something.\n\n**Shape of the result**\n- The success path returns a well-defined value (order id, created record), and errors return/propagate the shape the client expects — not a stringly-typed catch-all.\n\nTwo structural notes:\n\n1. **Call the handler directly, not through a rendered component.** Server actions are directly reachable endpoints; their auth/validation logic needs its own test regardless of any client-side coverage.\n\n2. **Mock one layer down, not the action's collaborators at the top.** Stub the DB client and the external API/payment client, then run the real handler on top so the test actually proves the auth and validation code executes — not a mock that returns canned success.\n\nFor each failure case, the assertion is really \"the mutation did not happen,\" so pair every rejection test with a check on the downstream mock (e.g. the insert was never called). And make it deterministic: freeze time if the action stamps `createdAt` or builds any id/order-number from `Date.now()`.\n\nWhen you're done, confirm the new tests fail against the current happy-path-only implementation (or a deliberately broken one), not just that they pass — otherwise they may be asserting nothing.",
862
+ "outputSha256": "b9ca622c6c3730eefea131a98c8c91221e89ea0fb614235e376dea5dd508f4b3",
863
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
864
+ "deterministic": [],
865
+ "judge": {
866
+ "verdict": "pass",
867
+ "reason": "Covers auth failure paths ('No session / unauthenticated caller → rejected', 'Authenticated but not allowed... → rejected'), input validation failures ('Missing required fields', 'Malformed values: wrong types, negative...'), and structural note 1 says 'Call the handler directly, not through a rendered component' with note 2 'Stub the DB client... run the real handler on top'. No fail criterion met."
868
+ },
869
+ "passed": true
870
+ },
871
+ {
872
+ "output": "At minimum, add tests for these, calling the exported action directly with constructed args (not through a rendered component):\n\n**Auth / authorization**\n- No session at all → should reject, and the mutation must not run.\n- Session present but not allowed to act on this resource (wrong user, wrong tenant/org, missing role) → reject.\n- If the action takes an id that could belong to someone else, test the cross-tenant/cross-user case explicitly — this is the one people most often forget.\n\n**Validation (each of the fields the action reads)**\n- Missing/undefined required field.\n- Wrong type (string where a number is expected, array vs. scalar).\n- Empty string, whitespace-only, and boundary values (0, negative quantity, an over-long string, an invalid enum value).\n- Malformed input overall (null/undefined args, extra unexpected fields, a body that isn't shaped like the expected payload).\n- Verify the rejection is a clean expected result (returned error / thrown domain error) and not an unhandled crash leaking a 500-ish shape.\n\n**Idempotency / duplicate handling**\n- Submitting the same logical order twice (same idempotency key, or a retried request) → does not create two orders, or fails predictably.\n\n**Failure modes from downstream dependencies**\n- DB/client throws (connection error, constraint violation, unique-key conflict) → the action surfaces a sane error and doesn't leave partial state.\n- A partial write: if the action writes more than one row/record, test the case where the second write fails and confirm whether the first is rolled back.\n\n**Side effects**\n- Any email, queue publish, webhook, or cache invalidation that should fire on success actually fires exactly once.\n- Confirm none of those side effects fire on the validation/auth failure paths.\n\n**Shape of the success return**\n- Assert the returned value (order id, status, whatever the caller relies on), not just that it didn't throw.\n\nTwo conventions to hold to while writing these:\n\n- Mock at the network/dependency boundary — mock the DB client or external API client one layer down and call the real action on top, so the test actually proves the action's own auth/validation logic ran. Don't stub the action or its internals.\n- Don't let it be only-ever-happy-path by structure: for each failure test, assert both the result *and* that no order/row/side-effect occurred, otherwise a test that passes could still be creating the order before rejecting.\n\nIf any of these depend on `Date.now()` or a generated id, freeze/inject that so the assertions are deterministic.",
873
+ "outputSha256": "64244faa41ce6ad5a08758e15a4e3310380f0d1fad7b040a48e936c9a5027534",
874
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
875
+ "deterministic": [],
876
+ "judge": {
877
+ "verdict": "pass",
878
+ "reason": "Answer names auth failure paths (no session, wrong user/tenant/role, cross-tenant) and validation failures (missing/undefined, wrong type, empty, boundary, malformed args). It explicitly says to call the exported action directly with constructed args, mocking the DB client one layer down, not through a rendered component. No fail criterion present."
879
+ },
880
+ "passed": true
881
+ },
882
+ {
883
+ "output": "Cover the failure paths and the boundary conditions, not just the success path. For a server action that \"creates an order,\" the happy-path test proves the mutation works when everything is fine — it says nothing about what happens when it isn't. Specifically:\n\n**Auth.** A server action is a directly reachable endpoint; anyone can POST to it. The test suite must prove the action *rejects* an unauthenticated caller and a caller with insufficient permissions, and that it does so *before* touching the database or external clients. Assert both the rejection *and* that no write happened (e.g., the mocked DB create was never called). This is the single most important missing test for a server action specifically, because client-side guards do not protect this path.\n\n**Validation.** Empty/missing required fields, malformed types, negative or zero quantity, a total that doesn't match the line items, oversized payloads. For each, assert the action returns/throws the expected validation error and again that no downstream mutation fired. Validation that runs but doesn't *block* the write is a common real bug, so assert the side-effect suppression, not just the returned message.\n\n**Downstream failure.** The DB client or external service throws (timeout, unique-constraint violation, payment decline). The test should mock that dependency one layer down and call the real handler on top, then verify the action surfaces a clean error and — critically — leaves no partial state (no orphaned order row, order not marked paid if the payment call failed). This is where the real bugs live: partial writes on the failure half of a multi-step creation.\n\n**Idempotency / double-submit.** If the action can be invoked twice (retry, double-click, network replay), test it. Either it deduplicates or you've documented that it doesn't — but you should know.\n\n**Revalidation.** If the action calls `revalidatePath`/`revalidateTag`, assert it fires on success and, depending on your policy, does not fire on validation failure.\n\n**Determinism.** Anything time- or randomness-dependent — order number generation, `createdAt`, tax/currency rounding at a boundary — must be frozen or injected, or the test will eventually go flaky and someone will skip it instead of fixing it.\n\nHow to structure it: keep calling the real exported handler with constructed args, and mock only at the network/persistence boundary (the DB client, the payment client) — not the action's own internals. That way the tests actually exercise the handler's auth and validation logic rather than a stubbed version of it. If you're using MSW for any HTTP calls the action makes, mock there rather than stubbing the fetch/DAL layer above it.\n\nFinally, confirm the tests are meaningful: the auth and validation tests should fail against a version of the action with those checks removed. If they pass either way, they aren't testing the guard.",
884
+ "outputSha256": "f14b55984c638d5785bd2659306a6a33a06e780118c6006e525236da47c3b062",
885
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
886
+ "deterministic": [],
887
+ "judge": {
888
+ "verdict": "pass",
889
+ "reason": "Auth failure path named: 'rejects an unauthenticated caller and a caller with insufficient permissions' (criterion 1). Validation failure path named: 'Empty/missing required fields, malformed types, negative or zero quantity' (criterion 2). Real handler with mocked downstream: 'keep calling the real exported handler... mock only at the network/persistence boundary (the DB client)' (criterion 3). No fail criterion met."
890
+ },
891
+ "passed": true
892
+ },
893
+ {
894
+ "output": "For a Server Action that creates an order, a happy-path-only test leaves the riskiest paths uncovered — the ones that actually decide whether the action is safe to expose to clients. Add coverage for:\n\n**Auth and authorization**\n- No session / unauthenticated request → action rejects and does not touch the DB or downstream client.\n- Authenticated but not permitted (wrong role, acting on another user's/tenant's data) → rejected. A create-order action that takes an `userId`/`orgId` from the client is the classic IDOR hole; assert the handler derives identity from the session, not the payload, and rejects a mismatch.\n\n**Validation**\n- Missing/invalid required fields (empty product, non-integer quantity, bad price/currency).\n- Boundary values: quantity 0, negative quantity, absurdly large quantity, negative or floating price.\n- Malformed/extra fields in the payload — confirm unknown fields aren't silently trusted.\nAssert both the return/throw shape and that the DB client was *not* called on a validation failure (a rejected input that still writes is a real bug).\n\n**Downstream failure handling**\n- The DB or payment/inventory client throws → the action surfaces a safe error and doesn't report success.\n- Partial failure / transaction rollback: if the order writes and a subsequent step fails, assert you don't leave a half-created order. This is the path that usually has the actual bug.\n- Idempotency, if relevant: calling the action twice with the same request doesn't double-create.\n\n**How to structure it**\n- Call the exported action directly with a constructed request/args — don't route it through a rendered component, since the point is the handler's own logic.\n- Mock one layer down (the DB client, the external API client), not the action itself, so the real auth/validation branches execute.\n- Cache/revalidate behavior: if the action calls `revalidatePath`/`revalidateTag`, assert it's invoked on success and skipped on failure.\n\n**Determinism**\n- Freeze time if the action stamps `createdAt`/order IDs, and stub any ID generator — otherwise the \"extra\" tests you add will be the flaky ones.\n\n**Verification before you call it done**\n- Run the suite; confirm each new test fails against a deliberately-broken version of the corresponding check (remove the auth guard, the quantity check, the rollback) and passes against the real code. A test that never goes red on the bug isn't testing anything.\n- At minimum, the action should end up with an explicit unauthenticated-rejection test and a validation-failure test — those two are the ones people most often forget and the ones an attacker hits first.\n\nSketch of the shape (Jest/Vitest, handler called directly):\n\n```ts\nimport { createOrder } from \"./actions\";\n\nvi.mock(\"@/lib/db\", () => ({ db: { order: { create: vi.fn() } } }));\n\nbeforeEach(() => vi.setSystemTime(new Date(\"2024-01-01T00:00:00Z\")));\n\nit(\"rejects unauthenticated\", async () => {\n mockSession(null);\n await expect(createOrder(validPayload)).rejects.toThrow(/unauth/i);\n expect(db.order.create).not.toHaveBeenCalled();\n});\n\nit(\"rejects invalid quantity without writing\", async () => {\n mockSession(user);\n await expect(createOrder({ ...validPayload, quantity: 0 })).rejects.toThrow();\n expect(db.order.create).not.toHaveBeenCalled();\n});\n\nit(\"rolls back on downstream failure\", async () => {\n db.order.create.mockRejectedValueOnce(new Error(\"db down\"));\n await expect(createOrder(validPayload)).rejects.toThrow();\n // assert no partial order / compensating delete happened\n});\n```\n\nSince this touches a Server Action's auth and validation paths specifically, that's in scope for the nextjs-nuxt-testing skill (which is also where the \"don't mock the data-fetching layer, do assert the auth/validation failure paths\" rules live) — worth loading it if you want the fuller conventions.",
895
+ "outputSha256": "c95f1c0cee14fb53255c1c7ccd52491a388a7afeef2772b6e0faca1f876841a1",
896
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
897
+ "deterministic": [],
898
+ "judge": {
899
+ "verdict": "pass",
900
+ "reason": "Answer names unauthenticated/unauthorized rejection tests (\"rejects unauthenticated\", wrong role/IDOR), validation failure paths (missing/invalid fields, quantity 0, negative), and shows calling the exported action directly with mocked DB (vi.mock db, createOrder(validPayload)). No claim that client-side validation suffices."
901
+ },
902
+ "passed": true
903
+ },
904
+ {
905
+ "output": "At minimum, add failure-path coverage for the auth and validation logic that lives *inside the handler itself*, plus the downstream failure modes — not just the success case. Concretely:\n\n1. **Unauthenticated / unauthorized callers.** No session, expired session, and a valid session for a user who isn't allowed to create this order (wrong role, wrong tenant/account). Assert both that it throws/returns the error *and* that the mutation was never called. A test that only checks the thrown error still passes if the DB write happened first — assert on the dependency, not just the return value.\n\n2. **Validation failures.** Missing required fields, empty strings, wrong types, out-of-range quantities, malformed IDs. Assert the downstream client is not reached. If validation and auth are separate guards, test each independently.\n\n3. **Downstream dependency failures.** DB/external API throws, times out, or returns a constraint violation. Assert the handler surfaces a sane error and doesn't leave partial state (no half-written order). This is the failure mode most likely to bite in production.\n\n4. **Idempotency/duplicate handling**, if the action can be retried or double-submitted — same idempotency key twice shouldn't create two orders.\n\n5. **Input the client would never send but an attacker can.** The server action is a directly reachable endpoint, so client-side validation is irrelevant here. Test that server-side validation rejects a crafted payload that bypasses the form.\n\nOn technique:\n\n- **Test the handler directly**, calling the exported function with constructed args, not through a rendered component.\n- **Mock one layer down** — the DB client / external API client — and run the real handler on top, so the test actually proves the auth and validation branches execute. Don't stub the handler's own internals.\n- **Assert on side effects**, not just the return value: was the insert called, with what args, or was it correctly *not* called.\n- Build inputs from a fixture/factory for realistic shape rather than hand-rolled partial objects.\n\nClient-side validation tests don't substitute for any of this — the handler is reachable without the client.\n\nOne caveat on scope: if part of the auth/validation actually lives in middleware or a shared guard rather than inside the handler, test that at its own level too, and keep the handler test focused on what the handler itself owns. And per the verification bar, confirm each new test fails against the pre-fix code before trusting it.",
906
+ "outputSha256": "3f88c0a890b402c6da6df1ff21b619c044a485a5d8ad27bfddf133b9688e1492",
907
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
908
+ "deterministic": [],
909
+ "judge": {
910
+ "verdict": "pass",
911
+ "reason": "Answer names unauthenticated/unauthorized caller tests (point 1, asserting mutation never called), validation failure tests (point 2: missing fields, wrong types, malformed IDs), and says to test the handler directly with mocked DB layer (technique section: 'Test the handler directly, calling the exported function', 'Mock one layer down — the DB client'). Also states client-side validation doesn't substitute. No fail criterion met."
912
+ },
913
+ "passed": true
914
+ },
915
+ {
916
+ "output": "Cover the paths that don't exist yet in your test, at minimum:\n\n- **Auth failure**: unauthenticated caller — session/`auth()` returns null. Assert the action rejects (or returns your failure shape) and, importantly, that no write happens (spy on the DB client and assert zero calls).\n- **Authorization failure**: authenticated but not permitted for this resource/user. This is distinct from auth — a logged-in user acting on someone else's cart/account.\n- **Validation failures**, one per rule, not one combined test: missing/malformed required fields, bad types, negative or zero quantity, invalid SKU/product id, oversized payloads, malformed JSON. Assert the action returns the expected field-level error and again that no mutation ran.\n- **Downstream failure**: DB/external API throws or times out. Assert the action surfaces a clean failure rather than leaking the raw error/stack to the client, and that you don't leave a partial order behind (transaction rollback or compensating behavior).\n- **Concurrency/duplicate submission**: double-submit or a retried request creating two orders. If you have an idempotency key or unique constraint, assert the second call does not create a duplicate row.\n- **Boundary/edge inputs**: empty string vs. whitespace-only name, max-length fields, unicode, price/quantity at integer and decimal limits, currency/precision rounding.\n- **Side effects**: assert the actual write payload — correct user id, correct totals computed server-side (not trusting a client-supplied total), correct initial status, and that no side effect fires on the failure paths above.\n\nShape and process:\n\n- Call the exported action directly with a constructed request/args; don't route it through a rendered component.\n- Mock at the network/DB boundary one layer down (the DB client or external API client), not the action itself, so the real validation and auth logic actually runs.\n- Don't hand-build partial mocks of the auth/session/request object that only satisfy today's happy path — use a realistic fixture/factory so failure shapes are representable.\n- Freeze time (`vi.setSystemTime` or an injected clock) anywhere the action stamps created-at, computes a deadline, or formats a date, otherwise the test is nondeterministic.\n- Give each failure path its own test so a regression in one rule doesn't get masked by another.\n\nVerification before calling it done: the new failure-path tests should fail if you temporarily remove the corresponding auth/validation/rollback code, and pass with it in place.",
917
+ "outputSha256": "bfc31dcf9a0fef173888ffb37dda0418c8e7c62434942e31a9ab002e5fdf183b",
918
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
919
+ "deterministic": [],
920
+ "judge": {
921
+ "verdict": "pass",
922
+ "reason": "Names auth failure (unauthenticated session/auth() null) and authorization failure as missing coverage; names validation failures (missing/malformed fields, bad types, negative quantity, invalid SKU) as missing coverage; and states to call the exported action directly with mocked DB client so real auth/validation runs. No fail criterion met."
923
+ },
924
+ "passed": true
925
+ },
926
+ {
927
+ "output": "At minimum, add coverage for these, since a Server Action is a directly reachable endpoint and its own logic has to stand on its own:\n\n**Auth and validation failure paths**\n- Unauthenticated request (no session / expired session) returns the expected rejected result or throws the expected error, and — importantly — does not touch the DB/order-creation path.\n- Unauthorized but authenticated (wrong user/role) if the action is user- or role-scoped.\n- Invalid input: missing required fields, wrong types, malformed IDs, out-of-range quantities/prices. Assert it fails before any mutation happens.\n- Boundary cases: empty cart/line items, zero or negative quantity, an item that no longer exists or is out of stock, currency/price mismatch if server recomputes totals.\n\n**Idempotency and concurrency**\n- If the action can be retried or double-submitted, assert a second invocation with the same idempotency key/payload doesn't create a duplicate order.\n- If there's any race concern (stock decrement, coupon redemption), test the rejecting path when the underlying state changed between read and write.\n\n**Downstream failure handling**\n- DB/client error mid-mutation: assert the action surfaces the right error and, if there's a transaction, that it rolls back rather than leaving a half-created order. Mock the DB/API client one layer down and call the real handler on top, so this test actually exercises the handler's error handling.\n- External dependency returns an error status, times out, or returns an unexpected shape.\n\n**Return contract**\n- Assert the exact shape of the success and failure return values (the discriminated result the caller revalidates/redirects on), not just \"it didn't throw.\"\n- Any `revalidatePath`/`revalidateTag`/redirect side effect that the caller depends on fires on success and not on failure.\n\n**Correctness of the mutation itself**\n- Persisted order contents match the input after server-side normalization (trimmed values, server-recomputed totals, server-assigned IDs/timestamps), so a tampered client payload can't set a price or user ID the server should own.\n\n**Determinism**\n- If the action or its result touches `Date.now()`, order IDs, or locale-formatted output, freeze/inject the clock and assert against a fixed value rather than whatever today happens to be.\n\nTwo things worth calling out on method: mock the downstream dependency (DB/external client) one layer down and exercise the real handler on top, so the auth/validation logic actually runs; and don't lean on the client-side validation tests you already have — this action is reachable without the client, so its own validation and auth checks need their own tests. When you're done, also confirm each new test fails against the pre-fix action before it passes, so you know it's exercising the behavior and not just the happy path again.",
928
+ "outputSha256": "63077d57a8fb25b2d41188518b1717d632c6035ebd87677693940cf286629d8d",
929
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
930
+ "deterministic": [],
931
+ "judge": {
932
+ "verdict": "pass",
933
+ "reason": "Answer names unauthenticated/unauthorized failure paths ('Unauthenticated request... returns the expected rejected result', 'Unauthorized but authenticated'), input-validation failures ('missing required fields, wrong types, malformed IDs'), and states to 'mock the downstream dependency (DB/external client) one layer down and exercise the real handler on top'. It also explicitly says not to lean on client-side validation tests. No fail criterion met."
934
+ },
935
+ "passed": true
936
+ },
937
+ {
938
+ "output": "A happy-path-only test on a Server Action leaves the highest-risk paths untested. For an order-creation action, add coverage for at least these:\n\n**Authorization**\n- No session / unauthenticated caller → rejected, no order written.\n- Authenticated but wrong tenant/owner acting on another user's resource → rejected.\n- Whatever role/permission gate exists (admin vs. regular user) → tested explicitly, not assumed from the happy path's logged-in state.\n\n**Validation (the action's own input validation, not the client's)**\n- Missing required fields, wrong types, malformed IDs.\n- Boundary values: quantity 0, negative quantity, empty line items, empty cart.\n- Out-of-range or invalid price/currency/tax inputs.\n- Oversized payloads, if the action accepts anything user-controlled.\n- A directly-crafted request that bypasses the client form entirely — this is the whole point, since the action is a reachable endpoint on its own.\n\n**Business-rule / state failures**\n- Insufficient inventory, out-of-stock item.\n- Invalid or expired discount/coupon.\n- Duplicate submission / idempotency: calling the action twice with the same idempotency key (or a double-click) should not create two orders.\n- Payment/charge failure at the downstream boundary → order not persisted, or rolled back.\n\n**Downstream failure handling**\n- DB client / external payment or inventory client throws → the action returns a clean error and leaves no partial order behind (verify the rollback/transaction path, don't just check the return value).\n- The failure returned to the client does not leak internal error detail/stack traces.\n\n**Determinism hooks**\n- Anything using `Date.now()` for order timestamps, order numbers, or expiry — freeze the clock (`vi.setSystemTime` / injected clock) so the assertion is stable.\n- Any `Math.random()` or generated IDs, and any locale/currency-formatting output.\n\n**How to test it**\n- Call the exported action function directly with constructed args (and a mocked session/request context), not through a rendered component.\n- Mock one layer *down* — the DB client or external API client — and run the real handler on top of it. That way the auth and validation branches actually execute rather than being stubbed around.\n- For each failure case, assert both the returned/redirected outcome **and** that no order row was created (or that the write was rolled back), so a test can't pass just because the error message looked right.\n\nPriority order if you can't cover everything at once: unauthenticated rejection, validation rejection, and idempotency — those are the three most likely to be exploitable in production, and none of them are exercised by a happy-path test.",
939
+ "outputSha256": "db756e2a6e8866a963eed2d6ca5c3c0b978e39e843e880f70b04436dac932cc4",
940
+ "promptSha256": "adc8bad9a532f671f6f42320f6497319924cbf30156e6c5d3a4654934f0a2600",
941
+ "deterministic": [],
942
+ "judge": {
943
+ "verdict": "pass",
944
+ "reason": "Answer names auth failure paths (unauthenticated caller rejected, wrong tenant/owner, role gate) and validation failures (missing fields, wrong types, boundary values, bypassing client form). It explicitly says to call the exported action directly with mocked session and mock one layer down (DB client) running the real handler, not through a rendered component. No fail criterion holds."
945
+ },
946
+ "passed": true
947
+ }
948
+ ]
949
+ }
950
+ ],
951
+ "verdict": "fail",
952
+ "scope": "bundled",
953
+ "skillDigest": "6a26a604911034a70b28a38d418062bc4713ffc99470b261a90d499f4a62d463",
954
+ "catalogDigest": "139cc7e7c63f9a3fcb3560551b7740c5642c828db2570e2f40728089218cc6d4",
955
+ "judgePromptVersion": "2026-09-25.1",
956
+ "runner": "deepseek",
957
+ "model": "deepseek-chat",
958
+ "runnerPromptVersion": "2026-09-25.1",
959
+ "recordedAt": "2026-09-25T15:00:31.740Z",
960
+ "judge": "deepseek",
961
+ "judgeModel": "deepseek-chat"
962
+ },
963
+ {
964
+ "schemaVersion": "1.0.0",
965
+ "skillId": "nextjs-nuxt/nextjs-nuxt-code-review",
966
+ "strictness": "high",
967
+ "trials": 10,
968
+ "triggerAccuracy": {
969
+ "truePositive": 4,
970
+ "falsePositive": 3,
971
+ "positives": 7,
972
+ "negatives": 6
973
+ },
974
+ "evidence": "authored",
975
+ "scenarios": [
976
+ {
977
+ "id": "trigger-positive-1",
978
+ "kind": "trigger-positive",
979
+ "prompt": "Review this Next.js server action diff for missing auth checks before it merges",
980
+ "strictness": "high",
981
+ "trials": 1,
982
+ "passes": 1,
983
+ "passRate": 1,
984
+ "passAtK": 1,
985
+ "grader": "trigger-rank-fork-family",
986
+ "status": "ran",
987
+ "deterministic": true
988
+ },
989
+ {
990
+ "id": "trigger-positive-2",
991
+ "kind": "trigger-positive",
992
+ "prompt": "In the app router pages touched by this diff, can you check for any 'use client' boundary mistakes?",
993
+ "strictness": "high",
994
+ "trials": 1,
995
+ "passes": 1,
996
+ "passRate": 1,
997
+ "passAtK": 1,
998
+ "grader": "trigger-rank-fork-family",
999
+ "status": "ran",
1000
+ "deterministic": true
1001
+ },
1002
+ {
1003
+ "id": "trigger-positive-3",
1004
+ "kind": "trigger-positive",
1005
+ "prompt": "There's a new server/api/orders.ts route in this Nuxt app that needs review, can you check it for security issues?",
1006
+ "strictness": "high",
1007
+ "trials": 1,
1008
+ "passes": 1,
1009
+ "passRate": 1,
1010
+ "passAtK": 1,
1011
+ "grader": "trigger-rank-fork-family",
1012
+ "status": "ran",
1013
+ "deterministic": true
1014
+ },
1015
+ {
1016
+ "id": "trigger-positive-4",
1017
+ "kind": "trigger-positive",
1018
+ "prompt": "One of the reviewers on my team flagged that a composable in this PR might be running too late in the component lifecycle -- can you take a look and confirm whether that's a real problem?",
1019
+ "strictness": "high",
1020
+ "trials": 1,
1021
+ "passes": 0,
1022
+ "passRate": 0,
1023
+ "passAtK": 0,
1024
+ "grader": "trigger-rank-fork-family",
1025
+ "status": "ran",
1026
+ "deterministic": true
1027
+ },
1028
+ {
1029
+ "id": "trigger-positive-5",
1030
+ "kind": "trigger-positive",
1031
+ "prompt": "This product page is marked for static generation, but the data behind it changes every few minutes -- before I approve this PR, can you sanity-check that the caching story here actually makes sense?",
1032
+ "strictness": "high",
1033
+ "trials": 1,
1034
+ "passes": 0,
1035
+ "passRate": 0,
1036
+ "passAtK": 0,
1037
+ "grader": "trigger-rank-fork-family",
1038
+ "status": "ran",
1039
+ "deterministic": true
1040
+ },
1041
+ {
1042
+ "id": "trigger-positive-6",
1043
+ "kind": "trigger-positive",
1044
+ "prompt": "Before this ships, can you check whether this component carries any hydration mismatch risk given how it renders?",
1045
+ "strictness": "high",
1046
+ "trials": 1,
1047
+ "passes": 1,
1048
+ "passRate": 1,
1049
+ "passAtK": 1,
1050
+ "grader": "trigger-rank-fork-family",
1051
+ "status": "ran",
1052
+ "deterministic": true
1053
+ },
1054
+ {
1055
+ "id": "trigger-positive-7",
1056
+ "kind": "trigger-positive",
1057
+ "prompt": "Someone on the team tweaked the SPA-only flag for one of our nuxt.config.ts routes and opened a PR -- can you sanity-check the change before I approve it?",
1058
+ "strictness": "high",
1059
+ "trials": 1,
1060
+ "passes": 0,
1061
+ "passRate": 0,
1062
+ "passAtK": 0,
1063
+ "grader": "trigger-rank-fork-family",
1064
+ "status": "ran",
1065
+ "deterministic": true
1066
+ },
1067
+ {
1068
+ "id": "trigger-negative-1",
1069
+ "kind": "trigger-negative",
1070
+ "prompt": "Implement a new Next.js server action for creating an order",
1071
+ "strictness": "high",
1072
+ "trials": 1,
1073
+ "passes": 0,
1074
+ "passRate": 0,
1075
+ "passAtK": 0,
1076
+ "grader": "trigger-rank-fork-family",
1077
+ "status": "ran",
1078
+ "deterministic": true
1079
+ },
1080
+ {
1081
+ "id": "trigger-negative-2",
1082
+ "kind": "trigger-negative",
1083
+ "prompt": "Write a test for this Nuxt composable that calls useAsyncData",
1084
+ "strictness": "high",
1085
+ "trials": 1,
1086
+ "passes": 1,
1087
+ "passRate": 1,
1088
+ "passAtK": 1,
1089
+ "grader": "trigger-rank-fork-family",
1090
+ "status": "ran",
1091
+ "deterministic": true
1092
+ },
1093
+ {
1094
+ "id": "trigger-negative-3",
1095
+ "kind": "trigger-negative",
1096
+ "prompt": "Fix this nuxi typecheck failure on an auto-imported composable",
1097
+ "strictness": "high",
1098
+ "trials": 1,
1099
+ "passes": 1,
1100
+ "passRate": 1,
1101
+ "passAtK": 1,
1102
+ "grader": "trigger-rank-fork-family",
1103
+ "status": "ran",
1104
+ "deterministic": true
1105
+ },
1106
+ {
1107
+ "id": "trigger-negative-4",
1108
+ "kind": "trigger-negative",
1109
+ "prompt": "Migrate this Next.js pages router app to the app router",
1110
+ "strictness": "high",
1111
+ "trials": 1,
1112
+ "passes": 0,
1113
+ "passRate": 0,
1114
+ "passAtK": 0,
1115
+ "grader": "trigger-rank-fork-family",
1116
+ "status": "ran",
1117
+ "deterministic": true
1118
+ },
1119
+ {
1120
+ "id": "trigger-negative-5",
1121
+ "kind": "trigger-negative",
1122
+ "prompt": "Review this plain React component for accessibility issues, no App Router concern",
1123
+ "strictness": "high",
1124
+ "trials": 1,
1125
+ "passes": 0,
1126
+ "passRate": 0,
1127
+ "passAtK": 0,
1128
+ "grader": "trigger-rank-fork-family",
1129
+ "status": "ran",
1130
+ "deterministic": true
1131
+ },
1132
+ {
1133
+ "id": "trigger-negative-6",
1134
+ "kind": "trigger-negative",
1135
+ "prompt": "Review this Python data pipeline function for missing exception handling",
1136
+ "strictness": "high",
1137
+ "trials": 1,
1138
+ "passes": 1,
1139
+ "passRate": 1,
1140
+ "passAtK": 1,
1141
+ "grader": "trigger-rank-fork-family",
1142
+ "status": "ran",
1143
+ "deterministic": true
1144
+ },
1145
+ {
1146
+ "id": "flag-missing-server-action-auth",
1147
+ "kind": "behavior",
1148
+ "prompt": "Here's a Next.js server action: it's marked 'use server', takes an orderId and a new status, and calls db.order.update({ where: { id: orderId }, data: { status } }) with no other logic. What should this review flag?",
1149
+ "strictness": "high",
1150
+ "trials": 10,
1151
+ "passes": 10,
1152
+ "passRate": 1,
1153
+ "passAtK": 1,
1154
+ "grader": "regex+judge",
1155
+ "status": "ran",
1156
+ "trialRecords": [
1157
+ {
1158
+ "output": "This is a correctness-blocking finding, not a style nit. Here's what the review should flag:\n\n**Missing auth/authorization check (`rules/security.mdc`)**\n\nA `'use server'` action is a directly reachable endpoint — the client that calls it can be anything, not just your own form. Anyone can POST to that action with an arbitrary `orderId`. The action as written lets any caller change the status of *any* order in the system, including orders they don't own. Flag: file:line, and the fix — resolve the current session inside the action, then scope the update to it (e.g. `where: { id: orderId, userId: session.user.id }`) or explicitly verify ownership/permission before updating. Do not rely on a middleware check or on the client-side form that called it; the handler must enforce this itself.\n\n**Missing input validation (`rules/security.mdc`)**\n\n`orderId` and `new status` arrive as untrusted input. The action should re-validate both inside the action: `orderId` is the expected shape/type, and `status` is constrained to an allowed enum/set of values. As written, an arbitrary `status` string is written straight into the DB — a malformed or out-of-set value gets persisted, and it can also be a vector for unexpected state transitions.\n\n**Status-transition validation (correctness)**\n\nDepending on the domain, an arbitrary status may be a legal value but an illegal *transition* (e.g. jumping a cancelled order back to `shipped`). If the order lifecycle has rules, the action needs to read the current state and reject invalid transitions rather than blindly overwriting.\n\n**Missing cache invalidation (`rules/patterns.mdc`)**\n\nThis is a mutation that changes data likely displayed elsewhere. There is no `revalidatePath()`/`revalidateTag()` call, so any cached view of that order (or an order list) will keep showing stale data after a successful write. Flag the missing revalidate for the relevant path/tag.\n\n**No error handling / missing return contract**\n\nThe update has no try/catch and no defined return value. A thrown DB error surfaces as an opaque server error to the client; at minimum the action should return a typed success/failure result the caller can act on.\n\n**Note on scope:** the review should *report* these — it should not rewrite the action. A single-action diff like this that's marked `'use server'` and does an unscoped write is exactly the case the \"the handler is a directly reachable endpoint regardless of what called it\" red flag warns against waving through.",
1159
+ "outputSha256": "c0c6b66aa7d419f4e499d5ae7a8bb24ec94505be1f997ce43b7136e5299da12a",
1160
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1161
+ "deterministic": [
1162
+ true
1163
+ ],
1164
+ "judge": {
1165
+ "verdict": "pass",
1166
+ "reason": "Answer flags missing auth as a correctness-blocking finding (not style), states a 'use server' action is a directly reachable endpoint callable by anything, and names the fix: resolve session inside the action and scope update to userId/session.user.id or verify ownership/permission for that specific order. No fail criterion holds."
1167
+ },
1168
+ "passed": true
1169
+ },
1170
+ {
1171
+ "output": "Flagging this against Step 6 (Server Action auth) and the Red Flags table, here's what to report:\n\n**File:line — the Server Action as written has no authorization check.**\n\nThe action is marked `'use server'`, which makes it a directly reachable endpoint on its own, regardless of what UI component calls it. There is no check that:\n\n- the caller is authenticated at all, and\n- the caller is authorized to change *this* order (e.g. it belongs to them, or they hold a role permitted to set that status).\n\nAny client can invoke it with an arbitrary `orderId`, so it's an IDOR / broken-access-control path: it lets one user change another user's order status. Per `rules/security.mdc`, this is a **correctness-blocking finding**, not a style nit.\n\n**Fix:** inside the action, resolve the session (e.g. the auth helper or `cookies()`/session read) and check ownership or role before the `db.order.update` call — return an error/throw if it fails, don't rely on the calling form having hidden the button.\n\n**File:line — no input validation on `status` or `orderId`.**\n\nThe action trusts the caller-supplied `status` string. Nothing constrains it to the set of legitimate status values, so a client can write an arbitrary value into the DB. Re-validate both arguments inside the action (e.g. a Zod/enum schema), not just in the client-side form. Same `rules/security.mdc` reasoning: the handler must re-validate its own input.\n\n**Also worth a note (Step 4 — data freshness):** if this action mutates data that's rendered elsewhere, the action should call `revalidatePath()` / `revalidateTag()` for the affected route(s). The diff as described has no revalidation, so the UI can keep showing the stale status after a successful write.\n\nThis skill reviews and reports only — I'm not editing the action; the report above names file:line and the concrete fix for each finding.",
1172
+ "outputSha256": "43e3af5905624fb769ac18c457027801ba88004b1104d0cea204f2d73b8d640d",
1173
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1174
+ "deterministic": [
1175
+ true
1176
+ ],
1177
+ "judge": {
1178
+ "verdict": "pass",
1179
+ "reason": "Answer flags missing auth as the finding ('no authorization check', 'correctness-blocking finding, not a style nit'), states the action is 'a directly reachable endpoint on its own, regardless of what UI component calls it', and names the fix: verify caller is authenticated and authorized to change 'this' order (ownership/role) before db.order.update, not relying on the calling form."
1180
+ },
1181
+ "passed": true
1182
+ },
1183
+ {
1184
+ "output": "This is a database write driven directly by client-supplied arguments with no server-side checks anywhere in the action. Under this review's Step 6 (Server Action auth) and `rules/security.mdc`, that is a correctness-blocking finding, not a style nit. Concretely:\n\n**1. No authentication check — the headline finding.** The action is a directly reachable POST endpoint regardless of what UI calls it. Nothing in the action establishes *who* is making the request. An `'use server'` action is not protected by being \"only called from my admin page\" — anyone who can reach the action ID can invoke it. Flag: missing session/auth check inside the action. Fix: resolve the session at the top of the action and bail if absent.\n\n**2. No authorization check on the specific resource.** Even with a valid session, there's no check that the caller owns/has rights over *this* `orderId`. This is an IDOR: any authenticated user can pass an arbitrary `orderId` and mutate someone else's order. The action takes `orderId` as a bare parameter — there is no ownership scoping in the query either (e.g. `where: { id: orderId, userId: session.userId }`) nor an explicit check. Flag the missing authorization, distinct from the missing authentication above.\n\n**3. No input validation.** `status` flows straight into the DB with no validation that it's a member of the allowed status set, and `orderId` with no validation that it's a well-formed id. Two consequences worth naming separately:\n - A client can write an arbitrary string into `status`, corrupting whatever invariant the column is supposed to hold. If the column is an enum, you get a runtime DB error instead of a clean 400; if it's a string, you get bad data silently.\n - `orderId` is passed unvalidated — depending on the id type this can also enable enumeration. Re-validate the shape server-side (zod/valibot), independent of any client-side form validation that called it.\n\n**4. Missing `revalidatePath()`/`revalidateTag()`.** Step 4: a mutation that changes displayed data must invalidate the relevant cached data. If the order's status is rendered anywhere that is cached (the order page, an admin list), a successful write leaves the UI showing stale data. Flag the missing revalidate — note it's only a real finding if the changed data is actually rendered from a cache, so say so and point at where to add it rather than asserting it unconditionally.\n\n**5. `'use server'` placement / boundary sanity (Step 2).** Confirm the directive sits at the top of the actual module (file-level) or on the single exported function, and that this file does not also export non-action helpers that would be exposed as endpoints. Also confirm the DB client imported here is server-only and not reachable from a Client Component import chain.\n\n**Rationalization to reject.** \"The caller validates the form / this is only called from an admin page\" does not excuse any of the above — the action is directly reachable, and the admin page is not an access control. Per the Red Flags table, flag the missing server-side check rather than waving it through.\n\n**Fix sketch (report, don't edit the file):** resolve session → verify caller has rights over `orderId` (or scope the `where` clause by owner) → validate `status` against the allowed set and `orderId` shape → return a typed result/error → call `revalidatePath()`/`revalidateTag()` for any affected cached view.\n\n**Report format:** each of the above as `file:line` with the concrete fix, with items 1–3 marked blocking.",
1185
+ "outputSha256": "ca664c59089d53ee3e96eaae3df219b52e1984f97441bcfe2c3fc3cd4cc90b7e",
1186
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1187
+ "deterministic": [
1188
+ true
1189
+ ],
1190
+ "judge": {
1191
+ "verdict": "pass",
1192
+ "reason": "Item 1 flags missing auth as headline/blocking finding; item 1 states action is 'directly reachable POST endpoint regardless of what UI calls it' and must check session itself; item 2 requires verifying caller rights over 'this orderId' (IDOR, ownership scoping where id+userId). No fail criterion: it explicitly rejects the 'admin page' rationalization."
1193
+ },
1194
+ "passed": true
1195
+ },
1196
+ {
1197
+ "output": "This is a blocking security finding, not a style nit.\n\n**File: the action itself, at the `'use server'` declaration and the `db.order.update` call.**\n\nThe action takes `orderId` and `status` from the client and goes straight to a database write with zero other logic. That means there is nothing in this function that:\n\n1. **Authenticates the caller** — no session/cookie check. A Server Action is a directly reachable POST endpoint (Next.js compiles it to a network call the client can invoke), not a private function. Anyone who can reach the app can call it with an arbitrary `orderId`.\n2. **Authorizes the action** — even if the caller is authenticated, nothing checks that *this* caller is allowed to change *this* order. A logged-in user can pass someone else's `orderId` and mutate their order. This is an IDOR / broken object-level authorization bug.\n3. **Validates its own input** — no schema check. `status` is written verbatim into the DB. Anything the column accepts (or a type-coercion surprise) goes through; an arbitrary string or an unexpected enum value is accepted. Input validation is not the caller's job — the form that called it can be bypassed entirely.\n\nThe key point for the report: **none of these can be delegated to the caller.** The client-side form, a middleware check, or a UI that \"only shows the button to admins\" are all bypassable, because the action is independently reachable. The action must re-check auth, re-check that the caller owns/is authorized for `orderId`, and re-validate `status` (e.g. against an allowed enum) inside the function body, before the `update` runs.\n\nPer `rules/security.mdc`, flag the missing auth/authorization/validation as correctness-blocking. This is the exact case covered in Step 6 of the workflow: a `'use server'` action that relies on anything outside itself for protection.\n\nSecondary note, only if the diff also shows a UI reading this data: if the write is meant to be reflected in a cached page or list, confirm a `revalidatePath()`/`revalidateTag()` follows the mutation (Step 4) — but that's secondary to the auth finding, which is the one that blocks the diff.",
1198
+ "outputSha256": "0f52c815881ec379667b606f6d4ab4eeae2888c8e6e72d0f96444549ef27c182",
1199
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1200
+ "deterministic": [
1201
+ true
1202
+ ],
1203
+ "judge": {
1204
+ "verdict": "pass",
1205
+ "reason": "Answer flags missing auth as a blocking security finding (not style). It states the Server Action is a directly reachable POST endpoint, not a private function, so protection can't be delegated to the caller/UI. It names the fix: authenticate the caller and verify the caller is authorized for this specific orderId (IDOR/broken object-level auth). No fail criterion holds."
1206
+ },
1207
+ "passed": true
1208
+ },
1209
+ {
1210
+ "output": "Flag the missing authorization and input validation inside the action itself. Step 6 of the review is explicit: every `'use server'` action must check auth/authorization and re-validate its own input inside the action, not rely on a middleware check or client-side form validation that called it. This action is a directly reachable endpoint — a POST to the action's endpoint can be constructed without ever going through the UI that was supposed to call it, so \"the caller already validates\" is not a defense. This is a correctness-blocking finding per the Red Flags table, not a style nit.\n\nConcretely, the review should report:\n\n- **No authorization check.** The action trusts `orderId` and `status` from the request body. Any authenticated (or, worse, unauthenticated, depending on how the action is exposed) caller can mutate any order by ID — a classic IDOR. The fix is to resolve the current session inside the action, verify the caller is allowed to change *that specific order* to *that specific status* (ownership, role, and any status-transition rules), and reject otherwise. Quoting `rules/security.mdc` by name is appropriate here per Step 7.\n- **No input validation.** `status` is written straight into `db.order.update`. There is no check that `status` is one of the allowed enum values, and no check that `orderId` is well-formed. An arbitrary status string can be persisted — clients can write invalid states that the rest of the app then has to defend against. Re-validate both fields inside the action (e.g., a schema parse) and reject invalid input with a proper error, not a silent write.\n- **Missing revalidation.** Step 4 covers this: a mutation that changes displayed data must call `revalidatePath()`/`revalidateTag()` (Next.js) or the UI will keep showing the stale order after a successful write. Note that whether this is squarely in scope depends on whether the diff touches a route that displays order status — if it does, flag it; if the action is consumed by a fully dynamic, uncached path, say so and downgrade rather than inventing a finding.\n- **No error handling / failure mode.** `db.order.update` throws on an unknown `id`. Confirm whether the diff lets that propagate as an unhandled server error or maps it to a sane error, and whether the action's return value tells the caller what happened. This is a secondary finding, not a blocker on its own, but worth naming in the report.\n\nWhat this diff does *not* require: boundary-directive checks (Step 2) — `'use server'` is correctly placed on an action that only runs server-side, and there's no `'use client'` boundary being moved here. Likewise Steps 3 and 5 (Nuxt composables, hydration mismatch) don't apply. Don't pad the report with those.\n\nOne caveat worth stating honestly in the review: because the action body is a single `db.order.update` call with no session lookup at all, I can't tell from the diff whether an upstream middleware or a wrapping helper is injecting an authorization check. The review should verify that — but the correct posture is that a control living in middleware, outside the action, is precisely the pattern `rules/security.mdc` tells you not to rely on, because the action is reachable independently of it. So the finding stands unless the \"middleware\" is actually an in-action session resolution that the diff is hiding.\n\nReport format, per Step 7: file:line for each finding, what's wrong, and the concrete fix — the auth check, the schema validation, and (conditionally) the revalidate call. Do not edit the code.",
1211
+ "outputSha256": "1a03e7a16928cb2f3a09d590dca5589085a8aa6a4a8b7ea0f04eec6bc7a1f71f",
1212
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1213
+ "deterministic": [
1214
+ true
1215
+ ],
1216
+ "judge": {
1217
+ "verdict": "pass",
1218
+ "reason": "Flags missing auth as correctness-blocking finding (\"No authorization check... classic IDOR\"), states action is directly reachable endpoint callable without UI (\"a POST to the action's endpoint can be constructed without ever going through the UI\"), and names fix: resolve session, verify caller allowed to change that specific order/status (ownership, role, transition rules). No fail criterion."
1219
+ },
1220
+ "passed": true
1221
+ },
1222
+ {
1223
+ "output": "Flag this as correctness-blocking, not a style nit — the action has no auth check and no input validation of its own.\n\n**1. Missing auth/authorization check (blocking)**\n\n`'use server'` marks this as a publicly reachable endpoint. Any client that knows or can guess an `orderId` can call it and change the order status to anything — including a caller who is not the order's owner, or not logged in at all. Middleware might block *pages*, but it does not reliably gate Server Action POSTs to the same route, and it is the wrong place to rely on regardless. The action must verify, inside the action itself, that the current session (e.g. `cookies()`/`headers()` → session lookup) is authenticated and authorized for *that specific orderId* — e.g. confirm the order belongs to the session's user, or that the user has an admin role — before the update runs. See `rules/security.mdc`.\n\n**2. No input validation (blocking)**\n\n`status` is passed straight into `db.order.update` with no check that it is one of the allowed status values. That is two problems at once:\n\n- **Unbounded state machine:** a caller can set `status` to any arbitrary string, including values the rest of the app never expects to handle (or a transition the app should forbid, e.g. jumping straight to `delivered`/`refunded`). Re-validate against the enum of legal statuses, and enforce legal *transitions* if the domain has them.\n- **Type/shape trust:** `orderId` and `status` are also unvalidated as to type/format. Validate both server-side (e.g. a schema parser) at the action boundary — never trust that the React form passed them correctly.\n\n**3. No cache invalidation after the write**\n\nA successful mutation that changes displayed order data needs `revalidatePath(...)`/`revalidateTag(...)` for the affected route(s)/tag(s), otherwise the order's page or list keeps showing the old status. This is not blocking the way auth is, but it is a concrete correctness gap — flag it.\n\n**4. Server-only access is fine, boundary is fine**\n\nThe `db` import is correctly confined to a `'use server'` file, so there's no leak of a DB client into a client bundle here. The problem is that the action's *only* logic is the raw mutation — everything that should guard it (auth, authorization scoped to `orderId`, validation, cache invalidation) is absent.\n\n**Report shape**\n\n- `app/.../actions.ts:<line>` — `'use server'` action updates any order by id with no session check → add an in-action auth + per-order authorization check before the update. *(blocking)*\n- `<line>` — `status` (and `orderId`) used unvalidated → validate against the allowed status enum/transitions at the action boundary. *(blocking)*\n- `<line>` — mutation completes with no `revalidatePath`/`revalidateTag` → invalidate the affected order view/tag.\n\nDo not rely on the calling form's client-side validation or a middleware check as the fix for #1 — the action is directly reachable regardless of what called it.",
1224
+ "outputSha256": "5e12433e1045f31ade4586106b3fc66fabfc77b39968e8d0d7f9c687a0a860c5",
1225
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1226
+ "deterministic": [
1227
+ true
1228
+ ],
1229
+ "judge": {
1230
+ "verdict": "pass",
1231
+ "reason": "Criterion 1: flags missing auth as 'correctness-blocking, not a style nit' and 'blocking'. Criterion 2: states \"'use server' marks this as a publicly reachable endpoint\" and \"the action is directly reachable regardless of what called it.\" Criterion 3: requires verifying session is authenticated and authorized for 'that specific orderId' (owner or admin role). No fail criterion holds."
1232
+ },
1233
+ "passed": true
1234
+ },
1235
+ {
1236
+ "output": "This is exactly the \"unauthenticated Server Action\" case from Step 6 / `rules/security.mdc`, and the diff here is the whole action — there's nothing else to check it against, which makes the finding straightforward but important.\n\n**What to flag**\n\n1. **Missing auth/authorization check — correctness-blocking, not a nit.**\n Any `'use server'` action is a directly reachable endpoint. The framework compiles it to a network-callable reference; nothing about `db.order.update` being called from a client-invoked path guarantees the caller is logged in, let alone that they own this order. The action as written will happily update **any** order ID supplied by **any** caller. This is the primary finding.\n\n The fix: inside the action, resolve the session (e.g. `cookies()`/`headers()`-based session lookup or your auth helper), 401/403 if absent, and then authorize — verify the resolved user actually has the right to mutate this specific order (ownership, tenant/org match, or an explicit role check). Don't rely on:\n - a middleware check upstream (middleware can be bypassed or misconfigured per-route, and this action may be callable from a path that doesn't match the matcher),\n - the client-side form that \"only shows the button to owners\" — that's UX, not an access control boundary,\n - the order ID being a UUID/opaque — that's obscurity, and IDs leak.\n\n2. **Missing input validation / re-validation inside the action.**\n `orderId` and `status` arrive from the client untrusted. Flag:\n - no validation that `status` is a member of the allowed status set (an unvalidated enum means a caller can set arbitrary/invalid states, or states the UI would never offer),\n - no validation/sanitization of `orderId` type/shape before handing it to the DB layer,\n - no handling of \"order not found\" — distinguish 404 from a successful no-op.\n\n3. **No ownership/tenant scoping in the update itself.**\n Even with a session, `where: { id: orderId }` alone doesn't constrain *whose* order it is. Prefer a scoped query (`where: { id: orderId, userId: session.user.id }` or equivalent) so authorization is enforced at the same layer as the write, not as a separate race-prone check-then-update.\n\n4. **No revalidation after the mutation — data-freshness fit (Step 4).**\n A successful write that changes displayed data should call `revalidatePath()`/`revalidateTag()` for whatever renders this order's status. Without it, the UI keeps showing stale data after a successful update. Flag it.\n\n5. **Missing error handling / return contract.**\n No try/catch or Result-style return means DB errors surface as opaque 500s and the caller can't distinguish success from failure. Not a security issue, but worth a line.\n\n6. **Audit/consistency (lower priority, mention briefly).**\n A status transition is often something you want to constrain by current state (no moving a `shipped` order back to `pending`) and/or log. The action has no such guard, so invalid transitions succeed. Flag as a correctness gap depending on your domain rules.\n\n**What *not* to flag here.** There's no `'use client'`/boundary issue to critique (nothing on the client side in this diff), no Nuxt composable, and no render-path nondeterminism — those steps are simply N/A for this action.\n\n**Bottom line for the report:** the headline finding is the missing in-action authentication *and* authorization (ownership) check, marked correctness-blocking; then input re-validation, scoped write, missing revalidation, and missing error handling as the secondary findings. Each should be reported with the file:line of the action and the concrete fix, and per Step 7 the code should not be edited — just reported.",
1237
+ "outputSha256": "d537bf1684c02e1885a4080721a558a644329cdd91692e1784c4b6b2c4d78e82",
1238
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1239
+ "deterministic": [
1240
+ true
1241
+ ],
1242
+ "judge": {
1243
+ "verdict": "pass",
1244
+ "reason": "Finding 1 explicitly flags missing auth/authorization as correctness-blocking, states a 'use server' action is a directly reachable endpoint callable by any caller, and the fix names resolving the session (401/403) plus authorizing mutation of this specific order (ownership/tenant/role). Also warns against relying on UI/middleware. No fail criterion holds."
1245
+ },
1246
+ "passed": true
1247
+ },
1248
+ {
1249
+ "output": "For a Server Action this thin — `'use server'`, `(orderId, newStatus)`, straight to `db.order.update` — the review should flag it as a correctness-blocking finding, not a style nit. Walking the checklist for this skill:\n\n**Step 6 (auth) — blocking finding.** The action itself contains no auth/authorization check and no input validation. This is the headline issue. A Server Action compiles to a POST endpoint with a stable, discoverable action ID; it's directly callable regardless of what UI normally invokes it. \"The form only renders for admins\" or \"the button is hidden for other users\" is not a control — it's a client-side affordance. The handler must check:\n- **Authentication** — is there a session at all?\n- **Authorization** — is the caller permitted to change *this* order's status? This is the subtler half: an authenticated user A must not be able to pass user B's `orderId` and mutate it. The check has to be the *same* predicate that reads the order (e.g. `where: { id: orderId, userId: session.user.id }`, or a fetch-then-check), not just \"is logged in.\"\n- **Input validation** — `newStatus` arrives as untyped/attacker-controlled input. If `status` is a constrained enum, an unvalidated value can write an invalid state (or, if the DB column is looser, worse). Validate against the allowed set inside the action.\n\n**A secondary shape issue worth naming.** As written, the `where` clause is `{ id: orderId }` alone. Even setting auth aside, ownership is not part of the mutation's own predicate, so there's no scoping at the data layer — the action trusts the `orderId` it was handed. Folding the ownership/authorization condition into the `where` (making the update a no-op if it doesn't match) is the more robust pattern than a separate check-then-update, which has a TOCTOU gap.\n\n**Step 4 (revalidate) — likely finding, needs confirming against the diff.** A mutation that changes displayed order data should call `revalidatePath()`/`revalidateTag()` for whatever displays it. If the action updates the row and returns without invalidating the relevant cached path/tag, the UI can keep showing the old status after a \"successful\" write. Whether this is a real finding depends on how the order is read/displayed elsewhere in the diff — if nothing cached it, this is weaker; if a cached page or `cacheTag`'d query shows status, it's concrete.\n\n**Not flagged here (be precise, don't pad).** There's no `'use client'` boundary, composable, `runtimeConfig`, or render-path `Date.now()`/`Math.random()` in this snippet, so the boundary, Nuxt-context, and hydration checks in this skill don't apply to *this* code — they'd apply to whatever consumes the action. I'd note that so the report doesn't imply coverage it didn't do.\n\n**Report format:** file:line for the action, the missing auth/authorization/validation as blocking, the unscoped `where` as the mechanism to fix it, and the revalidate concern flagged conditionally on how the order is displayed. Cite `rules/security.mdc` for the auth/validation finding and `rules/patterns.mdc` for the revalidation one. Report only — do not edit the action.",
1250
+ "outputSha256": "6d6d79d1cd175ee5f7790338fd7f1750eafb6014c256cecd6571ac593cb5261a",
1251
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1252
+ "deterministic": [
1253
+ true
1254
+ ],
1255
+ "judge": {
1256
+ "verdict": "pass",
1257
+ "reason": "Answer flags missing auth as 'correctness-blocking finding, not a style nit' (crit 1). States Server Action 'compiles to a POST endpoint... directly callable regardless of what UI normally invokes it' (crit 2). Names fix: authentication (session) plus authorization for 'this order's status', e.g. where {id, userId: session.user.id} (crit 3). No fail criterion: it explicitly rejects relying on UI checks."
1258
+ },
1259
+ "passed": true
1260
+ },
1261
+ {
1262
+ "output": "This review should flag the missing auth/authorization check and the missing input validation — this is a correctness-blocking finding, not a style nit (per `rules/security.mdc`).\n\n**What's missing:**\n\n1. **No auth/authorization check inside the action.** The action is marked `'use server'`, which makes it a directly reachable endpoint regardless of what calls it. Any client that can reach the app can invoke this action with an arbitrary `orderId` and `status`. There is no check that the caller is authenticated, and no check that the caller is authorized to modify *this specific order* — the classic IDOR (insecure direct object reference) shape: knowing or guessing an order ID is enough to mutate it. Relying on the form that calls it, or on middleware, does not satisfy this — middleware runs on the request path for navigations/route handlers, not as a guarantee for a Server Action's own logic, and the client-side form is trivially bypassable.\n\n2. **No input validation inside the action.** `orderId` is `where`-ed directly into a DB update and `status` is written directly into `data`. Neither is validated or parsed server-side. Two concrete risks:\n - **`status` unconstrained:** unless the DB column has a strict enum/check constraint, a caller can set `status` to any arbitrary string (or, depending on the ORM/column type, potential injection-shaped or type-coerced values). The action should validate `status` against the allowed set of status transitions, not just accept any value.\n - **No transition/state-machine check:** even a valid `status` value may be an illegal transition (e.g. moving a delivered order back to pending, or a caller setting a paid order to refunded). The review should flag whether an order state machine is expected here; the action currently allows any→any.\n\n3. **No return value / error handling to flag as a secondary note.** The action returns nothing and does not surface a failure path; callers can't distinguish success from a no-op. Worth a lower-severity note alongside the blocking findings.\n\n**What is *not* the primary finding here:** there's no `'use client'` boundary issue or revalidate concern in what's described (it's a straight mutation), and no hydration surface. Those checklist items simply don't apply to this snippet. The missing revalidate only becomes relevant if you're told the mutated order is displayed from cached/static data — if so, flag that a `revalidatePath()`/`revalidateTag()` for the affected order view is missing after the successful write.\n\n**Report shape:**\n\n```\nserver/actions/updateOrderStatus.ts:1-3 — BLOCKING: 'use server' action performs\ndb.order.update with no auth/authorization check; any caller can mutate any\norderId. Fix: resolve the session, verify the caller owns/can-modify this order,\nthen update; reject otherwise.\n\nserver/actions/updateOrderStatus.ts:1-3 — BLOCKING: no server-side validation of\norderId or status. Fix: validate status against the allowed values/transitions\nand validate orderId shape before the update; do not rely on the calling form.\n```\n\nPer the skill: do not edit the code under review — report file:line, what's wrong, and the concrete fix.",
1263
+ "outputSha256": "a33e708451a8439137f89d39851d52c5d49a38244b6a164d01d241ac4433435e",
1264
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1265
+ "deterministic": [
1266
+ true
1267
+ ],
1268
+ "judge": {
1269
+ "verdict": "pass",
1270
+ "reason": "Answer flags missing auth as BLOCKING finding (criterion 1: 'BLOCKING: ... no auth/authorization check'). States 'use server' makes it 'a directly reachable endpoint regardless of what calls it' and form/middleware reliance insufficient (criterion 2). Names fix: 'resolve the session, verify the caller owns/can-modify this order' and IDOR/'this specific order' (criterion 3). No fail criterion."
1271
+ },
1272
+ "passed": true
1273
+ },
1274
+ {
1275
+ "output": "This review should flag a correctness-blocking finding, not a style nit: the Server Action has no auth/authorization check and no input re-validation of its own.\n\n**What's missing (Step 6 / `rules/security.mdc`):**\n\n- **No auth check.** `'use server'` makes this an independently reachable endpoint (POST to the action's URL / action ID). Nothing in the action verifies that the caller is authenticated, let alone that they're allowed to change *this* order. Any user who can reach the app can pass an arbitrary `orderId` and `status`.\n- **No authorization / ownership check.** Even if the app has a middleware or layout-level session check, that does not prove the caller owns or may modify `orderId`. There's no check like \"does this order belong to the current session's user, or is this an admin.\" This is an IDOR-shaped hole: `orderId` is attacker-controlled and passed straight into the `where` clause.\n- **No input re-validation.** `orderId` and `status` are used directly. `status` should be validated against the allowed enum/state-machine transitions, not written as-is — otherwise a caller can set an arbitrary status string (or an invalid/unauthorized transition like skipping payment). `orderId` should be shape/type-validated before hitting the DB.\n\n**Why the usual counter-arguments don't hold (Red Flags):**\n\n- \"The form client-side validates it\" — the handler is directly reachable regardless of what called it; the client-side check is not a server boundary. Flag the missing server-side check.\n- \"Middleware already checks auth\" — middleware is a routing-layer gate; it does not establish that this user may mutate this specific record. The action must re-authorize for the specific `orderId`.\n\n**Concrete fix (report, don't apply — review skill edits nothing):**\n\n- Inside the action: resolve the session, reject if unauthenticated.\n- Load the order (or filter the update on ownership) and confirm the caller is permitted to change it; reject otherwise.\n- Validate `orderId` format and assert `status` is a permitted value/transition.\n- Only then run `db.order.update(...)`.\n\n**Also note (Step 4):** if a page/route displays this order's status and is cached/static, a successful update here needs a matching `revalidatePath()`/`revalidateTag()`; otherwise the UI shows stale status after the write. Worth flagging as a related data-freshness finding, though the missing auth/validation is the blocking one.\n\nReport with file:line for the action, mark the missing auth/authorization as correctness-blocking, and cite `rules/security.mdc`. Do not modify the file under review.",
1276
+ "outputSha256": "563437a66a34434a6bda4111ee842ff2bdfa77bc668e50de2d672246d3152cd3",
1277
+ "promptSha256": "285f25f7229195e38e01462656c86c2419ddcbafc2b452a0ee0ff55b1a4f51f3",
1278
+ "deterministic": [
1279
+ true
1280
+ ],
1281
+ "judge": {
1282
+ "verdict": "pass",
1283
+ "reason": "Flags missing auth as correctness-blocking finding (\"no auth/authorization check\"). States 'use server' makes it an independently reachable endpoint regardless of caller. Fix verifies caller authenticated and permitted to change this specific orderId (ownership/admin, IDOR). Rejects UI/middleware counter-arguments."
1284
+ },
1285
+ "passed": true
1286
+ }
1287
+ ]
1288
+ },
1289
+ {
1290
+ "id": "flag-page-level-use-client",
1291
+ "kind": "behavior",
1292
+ "prompt": "This Next.js diff adds 'use client' at the top of app/dashboard/page.tsx because one small stat card in the middle of the page needs a hover tooltip with local state. Everything else on the page is static server-rendered content. What should the review say?",
1293
+ "strictness": "high",
1294
+ "trials": 10,
1295
+ "passes": 10,
1296
+ "passRate": 1,
1297
+ "passAtK": 1,
1298
+ "grader": "regex+judge",
1299
+ "status": "ran",
1300
+ "trialRecords": [
1301
+ {
1302
+ "output": "**Finding: `'use client'` is misplaced — it's on the page, which drags the entire subtree into the client bundle.**\n\n`app/dashboard/page.tsx:1` — The directive is on the page component itself, so every component in the page's import chain (and the page's whole rendered subtree) ships as client JavaScript, not just the one stat card that needs hover state. That's the exact boundary mistake this review exists to catch. \"It's simpler to keep the page as one reviewing unit\" is not a justification here — it's the finding.\n\n**What's wrong, concretely:**\n\n- The page is otherwise static, server-rendered content. Moving `'use client'` to the page means that static content is now hydrated and shipped as client JS for no reason — larger bundle, slower hydration, and the static parts lose their Server Component benefits (no direct server-side data access, no zero-JS rendering).\n- Hover tooltip with local state is a leaf concern: it needs `useState`/`onMouseEnter` in exactly one component.\n\n**Fix:**\n\n1. Remove `'use client'` from `app/dashboard/page.tsx`, making the page a Server Component again.\n2. Extract the stat card (or just its tooltip-triggering part) into its own file — e.g. `app/dashboard/stat-card.tsx` (or a `components/` location) — and put `'use client'` there.\n3. Import and render that leaf Client Component from the Server Component page. Pass only the data the card actually needs to render as props (a plain serializable stat value/string), not the whole data object or any server-only values.\n\n**Boundary check on the surrounding code:**\n\n- Confirm the Server Component page isn't reading `cookies()`/`headers()` and passing raw session/auth data down to the newly-extracted Client Component — pass only what it renders.\n- Confirm no server-only import (DB client, API key, a `'use server'` file) sits in the import chain that now reaches the Client Component.\n\n**Not flagged:** the local state / hover tooltip requirement itself is fine — it legitimately needs a client boundary. The issue is *where* the boundary is drawn, not that one exists.\n\n**Blocking?** Not an auth/correctness blocker, but a real boundary finding: report it with `app/dashboard/page.tsx:1` and the extraction fix above rather than waving it through.",
1303
+ "outputSha256": "6c0a5b2c96d20c462b77c5dab205971edde39fa042dde9a80dfa5f0b8f12e1f2",
1304
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1305
+ "deterministic": [
1306
+ true
1307
+ ],
1308
+ "judge": {
1309
+ "verdict": "pass",
1310
+ "reason": "Answer states the page-level 'use client' is too broad ('drags the entire subtree into the client bundle', static content hydrated for no reason) and recommends extracting the stat card/tooltip into its own file (app/dashboard/stat-card.tsx) with 'use client', keeping page.tsx a Server Component. It rejects the 'simpler' tradeoff rather than approving it."
1311
+ },
1312
+ "passed": true
1313
+ },
1314
+ {
1315
+ "output": "This diff has a real finding, and it's the exact one this review exists to catch.\n\n**Finding — `app/dashboard/page.tsx:1`: misplaced `'use client'` boundary.**\n\nThe directive is on the page, but the only thing that needs client-side behavior is the stat card's hover tooltip (local `useState`). A page-level `'use client'` is inherited by every component in the subtree, so the entire dashboard — including all the static server-rendered content — is now shipped to the browser and re-executed there. That's the cost in bundle size and in lost Server Component capability (no direct `cookies()`/`headers()` reads, no server-only imports in this subtree, no async server data fetching below this point).\n\n**What the review should say to do:** keep `app/dashboard/page.tsx` as a Server Component (drop the directive), and extract just the stat card into its own small Client Component — e.g. `app/dashboard/StatCard.tsx` with `'use client'` at the top — then render that one component from the page. The rest of the page stays a Server Component. If the stat card needs data, pass it down as a prop from the page rather than having the card fetch it.\n\n**Also check while you're in here (Step 2):**\n- Does anything now under this page-level `'use client'` import a server-only module (DB client, API key, `'use server'` file)? If so, that import is now in a Client Component's import chain — flag it as a second, separate finding, since it will break at build time or leak server code.\n- Does the page read `cookies()`/`headers()` for auth data and pass raw session data down? With the boundary at the page level, that data now flows to the client. Pass only what the card actually renders.\n\n**Note on framing:** the diff author's rationale (\"only one small card needs this\") is not a reason the page-level directive is fine — it's the reason it's wrong. The smaller the genuinely-client piece relative to the page, the more you're paying to ship everything else.\n\nI did not review the rest of the diff; if it also touches Nuxt files, Server Actions, or `nuxt.config.ts`/routeRules, those need the matching checklist steps and should be reported separately. I have not edited any code.",
1316
+ "outputSha256": "7db3da5f9369e933237a99351fc10d0020ccb7f5eb0b9ad4f32ba66b1360d773",
1317
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1318
+ "deterministic": [
1319
+ true
1320
+ ],
1321
+ "judge": {
1322
+ "verdict": "pass",
1323
+ "reason": "Answer states page-level 'use client' is too broad: 'the only thing that needs client-side behavior is the stat card's hover tooltip... A page-level directive is inherited by every component in the subtree.' It recommends extracting the stat card into its own file 'app/dashboard/StatCard.tsx' with 'use client', keeping page.tsx as a Server Component. No fail criterion holds."
1324
+ },
1325
+ "passed": true
1326
+ },
1327
+ {
1328
+ "output": "**Finding — `app/dashboard/page.tsx`: `'use client'` is placed at the page level for one interactive leaf node.**\n\nThis is exactly the boundary mistake this review exists to catch. The page is a Server Component by default; adding `'use client'` at the top converts the entire page — and every component it imports without its own explicit boundary — into client-rendered code shipped to the browser. The stated reason (one small stat card needs a hover tooltip with local state) only requires the *stat card* to be a Client Component, not the page.\n\n**Concrete fix:**\n1. Remove `'use client'` from `app/dashboard/page.tsx`.\n2. Leave the page as a Server Component (keeps the static server-rendered content server-side, keeps the payload down).\n3. Extract the tooltip-bearing stat card into its own file, e.g. `app/dashboard/StatCard.tsx` (or `components/StatCard.tsx`), and put `'use client'` at the top of *that* file — the smallest component that actually needs browser state and an event handler.\n4. Import `StatCard` into the page and pass it the already-computed stat value as a prop. No session data, cookies, or server-only imports need to cross the boundary for a tooltip.\n\n**What this preserves:** the static content stays server-rendered, the interactive behavior is unchanged, and the client bundle shrinks to just the card instead of the whole dashboard subtree.\n\n**Secondary check while you're in this file:** confirm no server-only import (DB client, API-key read, a `'use server'` module) is now pulled into the page's import chain as a side effect of the misplaced directive — if the page was previously mixing server-only reads, the page-level `'use client'` may also have silently broken them, not just bloated the bundle.\n\n**Also worth noting in the report, not blocking:** a hover tooltip with local state is a candidate for a CSS-only solution (e.g. `:hover` plus a positioned element, or the native `title`/`popover` attribute) — if that satisfies the requirement, no `'use client'` is needed anywhere on this page at all. Mention it as an option; don't treat it as required.\n\n**Report format:** file:line for the directive, the finding above, and the extraction fix — do not edit `page.tsx` yourself.\n\n**Verification for this review:** confirm the `'use client'` placement was checked against the component that actually needs it (done — it was not), and confirm the extracted `StatCard` still receives everything it renders as a prop rather than reaching back into a server-only source.",
1329
+ "outputSha256": "43771c89d59829e6956a194e3fe2f91df60ccfafe2a42c00faa61e35dabc512c",
1330
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1331
+ "deterministic": [
1332
+ true
1333
+ ],
1334
+ "judge": {
1335
+ "verdict": "pass",
1336
+ "reason": "Answer states page-level 'use client' is too broad since only the stat card needs state ('only requires the stat card to be a Client Component, not the page'), and concretely recommends extracting it into its own file (app/dashboard/StatCard.tsx) with 'use client' there while the page stays a Server Component. No fail criterion holds."
1337
+ },
1338
+ "passed": true
1339
+ },
1340
+ {
1341
+ "output": "This is the exact case Step 2 of the workflow is written to catch — flag it, don't approve it.\n\n## Finding\n\n**`app/dashboard/page.tsx:1` — misplaced `'use client'` boundary (correctness/perf, not style)**\n\n- **What's wrong:** A page-level `'use client'` is applied to solve a need that exists in one small stat card. The directive is a boundary, not a per-component annotation: everything in the page's import chain below it — including the static server-rendered dashboard content and any server-only data fetching or imports it transitively pulls in — gets pulled into the client bundle and rendered on the client. The page's actual client-side need is local hover state in one leaf component.\n- **Fix:** Keep `app/dashboard/page.tsx` as a Server Component. Extract the stat card into its own file (e.g. `app/dashboard/stat-card.tsx`), put `'use client'` at the top of *that* file only, and have the Server Component page render `<StatCard ... />` as a child. Move only the props the card needs (displayed value/label) across the boundary.\n\n## Why it isn't acceptable here\n\n- The red-flags table already answers the likely pushback (\"'use client' is on the page, but that's fine, it's simpler to review as one unit\"): a page-level directive shipping the whole subtree to the browser *is* the finding this review exists to catch, not something to wave through for review convenience.\n- The presence of static content elsewhere on the page is not merely incidental — it's the harm. Static server-rendered content that gains nothing from client rendering now pays the client-bundle and hydration cost for a hover tooltip.\n- Before closing, verify the boundary claim against the actual import graph, not the diff's own intent: check whether the page imports anything server-only (a DB client, a secret read, a `'use server'` module) that would now sit in a Client Component's import chain. If so, that's an additional, more serious finding alongside the misplaced directive.\n\n## Not a finding\n\nThe tooltip's local state itself is a legitimate client concern — the review should say so explicitly, so the extraction isn't misread as \"remove the interactivity.\" The only thing being rejected is *where* the boundary was drawn.",
1342
+ "outputSha256": "41cecb1425c9b40244447eb4d771aa210a3fffc601715491f0b4c58865e5734e",
1343
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1344
+ "deterministic": [
1345
+ true
1346
+ ],
1347
+ "judge": {
1348
+ "verdict": "pass",
1349
+ "reason": "Answer states page-level 'use client' is too broad for one small stat card's hover state ('applied to solve a need that exists in one small stat card'), and recommends extracting the stat card into its own file (app/dashboard/stat-card.tsx) with 'use client' there only, keeping page.tsx a Server Component rendering <StatCard/>. It rejects the 'simpler as one unit' tradeoff rather than approving it."
1350
+ },
1351
+ "passed": true
1352
+ },
1353
+ {
1354
+ "output": "The review should flag this as a Server/Client boundary finding — correctness-blocking placement, not a style nit.\n\n**Finding:** `app/dashboard/page.tsx:1` — `'use client'` is applied at the page level for a single leaf component that needs local state.\n\n**Why it matters:** The directive marks the entire module *and its import subtree* as a client boundary. So the whole dashboard page — all the static server-rendered content — now ships as client JS, is hydrated in the browser, and can no longer use server-only imports (DB clients, secrets, `cookies()`/`headers()` reads) or Server Components further down the tree. That's a large cost for one hover tooltip.\n\n**Fix:** Extract the stat card into its own file, e.g. `app/dashboard/StatCard.tsx`, put `'use client'` at the top of *that* file only, and import it into the still-server `app/dashboard/page.tsx`. The tooltip's local state (`useState` for hover/open) lives entirely inside the client leaf. If the page's static content is genuinely static and needs no per-request data, it can stay a Server Component, and the page-level `'use client'` comes off.\n\n**Watch for in the same pass:**\n- Whether the page (or its new client boundary) reads `cookies()`/`headers()` or passes server-fetched data into the client card — pass only what the card actually renders, and don't route session data through a Client Component prop if the card doesn't need it.\n- Whether any server-only import (a DB client, an API key, a `'use server'` file) is now reachable from the client chain because the whole page became client-side.\n- If the card needs to trigger a write on interaction instead of just a tooltip, that should be a Server Action with its own auth/input validation (see `rules/security.mdc`) — a hover tooltip shouldn't be a reason to widen the client boundary.\n- Any `Date.now()` / `Math.random()` / locale-dependent formatting rendered inside the new client card, which is a hydration-mismatch source.\n\n**Verdict:** Request changes — move the directive down to the leaf component. No need to modify anything else in the diff; this is the one boundary correction. (Reporting only — this skill does not edit.)\n\nVerification note for the report: the placement of the `'use client'` was checked against what actually needs browser state (one card), not assumed correct because it \"works.\"",
1355
+ "outputSha256": "e2eb624e123a26cb02af1da2c517fe64f31e32e52b831121e69253ed2611d449",
1356
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1357
+ "deterministic": [
1358
+ true
1359
+ ],
1360
+ "judge": {
1361
+ "verdict": "pass",
1362
+ "reason": "Answer states page-level 'use client' is too broad for 'a single leaf component that needs local state' and that the whole static page ships as client JS. Fix concretely extracts stat card into app/dashboard/StatCard.tsx with 'use client' there only, imported into the still-server page.tsx. No fail criterion: it rejects the page-level directive, not approves it."
1363
+ },
1364
+ "passed": true
1365
+ },
1366
+ {
1367
+ "output": "This is a textbook case of the `'use client'` boundary being placed too high, and it's the exact pattern the Red Flags table calls out — don't wave it through.\n\n**Finding — file:line (app/dashboard/page.tsx, line 1)**\n\nThe `'use client'` directive is on the whole page, but only one stat card needs client-side interactivity (hover tooltip + local state). That drags the entire page subtree — including all the static, server-rendered content around it — into the client bundle.\n\nWhat's wrong:\n- The static content loses its Server Component status: it now ships to the browser as client JS (larger bundle, more hydration work) even though none of it needs state, events, or browser APIs.\n- The dashboard page can no longer do server-only work the way a Server Component page can — any `async` data fetch / direct DB access at the page level that reads `cookies()`/`headers()` is now unavailable (those are Server-Component-only APIs), which is a common downstream symptom of this exact slide.\n- It's the opposite of the \"smallest component that needs the boundary\" rule in Step 2.\n\nConcrete fix:\n- Extract the stat card into its own component (e.g. `app/dashboard/StatCard.tsx`) and put `'use client'` at the top of *that* file only.\n- Make `page.tsx` stay a Server Component and render `<StatCard ... />` with the stat values passed down as props.\n- If the tooltip is the only client behavior and the card's value is otherwise stable, this keeps the page server-rendered and limits the client bundle to the one card.\n- If the card also needs hover state only, consider whether a CSS-only tooltip would remove the need for `'use client'` entirely — worth raising as an option, not a requirement.\n\nSeverity: correctness-blocking for the review's stated purpose (misplaced Server/Client boundary), not a style nit — this is the finding the skill exists to catch.\n\n**What to confirm while reviewing**\n- Check the rest of the page's `page.tsx` for anything that now reads `cookies()`/`headers()` or imports a server-only module (DB client, server action file) — if present, those imports are now either broken or silently being pulled toward the client and should be flagged separately.\n- Verify no child of the newly-client page was itself importing something server-only; with page-level `'use client'`, that import chain is now suspect.\n\nNet review comment: move `'use client'` down onto the stat card component; keep `page.tsx` a Server Component.",
1368
+ "outputSha256": "71d423f13acadfe1b8d92d3dc00535c889928031ddf22bf45ddb6fdc08d82e5f",
1369
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1370
+ "deterministic": [
1371
+ true
1372
+ ],
1373
+ "judge": {
1374
+ "verdict": "pass",
1375
+ "reason": "Answer states page-level 'use client' is too broad since only one stat card needs interactivity ('boundary being placed too high', 'drags the entire page subtree... into the client bundle'). It concretely recommends extracting into app/dashboard/StatCard.tsx with 'use client' there only, keeping page.tsx a Server Component rendering <StatCard/> with props. No approval of page-level directive."
1376
+ },
1377
+ "passed": true
1378
+ },
1379
+ {
1380
+ "output": "Review finding — `app/dashboard/page.tsx`\n\n**Finding (boundary placement, blocking):** The `'use client'` directive is applied at the page level to support one small interactive stat card. That converts the entire dashboard page — all the static server-rendered content — into a Client Component. Everything on the page (the static content, and any imports it pulls in) is now bundled and shipped to the browser and rendered on the client, when only the tooltip card actually needs browser state. This is a Server/Client boundary mistake, not a style nit.\n\n**What's wrong, concretely:**\n- The client/server boundary is drawn at the wrong granularity. `'use client'` marks that file (and its whole import subtree, minus separately-marked boundaries) as client-rendered. Putting it on the page means the page — not the card — becomes the boundary.\n- Static content that could have been streamed as server-rendered HTML now depends on client JS to render, hurting first paint and bundle size.\n- It also risks dragging server-only imports (a DB client, an env-read module) into a client import chain if any of those static sections import server-side modules — the page file itself losing its server boundary can pull siblings toward the client graph.\n\n**Fix:** Remove `'use client'` from `app/dashboard/page.tsx`. Extract the stat card (the tooltip + local state) into its own component file, e.g. `app/dashboard/_components/StatCard.tsx` or `.../stat-card.tsx`, and put `'use client'` at the top of *that* file only. Import it into the page. The page stays a Server Component; only the card crosses the boundary. Per `rules/patterns.mdc`: keep `'use client'` on the smallest component that actually needs browser state, an event handler, or a browser API — not on a page or an ancestor of the component that needs it.\n\n**One follow-on to verify while you're in there:** confirm no Server Component on this page reads `cookies()`/`headers()` and passes raw session/auth data down as props to the card. Since the card is being pulled out anyway, pass it only the values it needs to render the tooltip, not an auth object.\n\n**Verdict:** Request changes. The directive must move off the page before this lands; the resulting rendering-mode/bundle regression is exactly what this review exists to catch and isn't something to wave through for review simplicity.\n\n(Separately, per Step 5: if the stat card renders `Date.now()`, `Math.random()`, or locale/timezone-dependent formatting in its render path, flag that too — now that it's a client component it still needs a guard or a stable server-computed value to avoid a hydration mismatch.)",
1381
+ "outputSha256": "6d8aa83faf3cbff6b79351551b893c0c5b279f0a6daa663f6b9884fa780f2562",
1382
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1383
+ "deterministic": [
1384
+ true
1385
+ ],
1386
+ "judge": {
1387
+ "verdict": "pass",
1388
+ "reason": "Answer states page-level 'use client' converts whole page to Client Component when only the tooltip card needs browser state (\"only the tooltip card actually needs browser state\"). Fix concretely extracts stat card into its own file (StatCard.tsx) with 'use client' there only, page stays Server Component. No approval of page-level directive."
1389
+ },
1390
+ "passed": true
1391
+ },
1392
+ {
1393
+ "output": "**Finding (boundary placement): app/dashboard/page.tsx — `'use client'` is on the page, not on the component that needs it.**\n\nWhat's wrong: the client boundary is set at the root of the dashboard route, so the entire page subtree — all the static server-rendered content plus every component under it — is pulled into the client bundle and hydrated, for the sake of one stat card's hover tooltip. This is the exact case flagged in Step 2: the directive should sit on the smallest component that actually needs browser state, not on a shared page/layout that merely contains it. Cost is real: larger JS payload, more hydration work, and the static content loses its ability to stay a pure Server Component (any server-only data fetching or imports in that subtree now has to be split back out or re-flagged).\n\nConcrete fix: extract the stat card (and its hover state) into its own file, e.g. `app/dashboard/StatCard.tsx`, with `'use client'` at the top of *that* file only. Import it as a leaf into `app/dashboard/page.tsx`, which stays a Server Component with no directive. Pass the already-resolved display values into it as plain serializable props from the server — do not pass cookies/headers/session objects down if only a number or label is rendered; pass just what the card displays. If the tooltip state can be done without JS at all (CSS `:hover` + pseudo-element or `title`), note that as the even-cheaper option worth considering, though the client island is the correct minimum if real state is needed.\n\n**Why this isn't a style nit:** per Step 2 and the Red Flags table, \"`'use client'` is on the page, but it's simpler as one unit\" is precisely the rationalization this review exists to reject — a page-level directive ships the whole subtree to the browser, which is the finding, not a reason to pass it.\n\n**Also check before closing:**\n- Nothing in the now-server parent is a server-only import that the extracted client card would need — if so, it must be split out separately, not imported into the client file (Step 2, third bullet).\n- If any data feeding the card is auth-gated or per-user, confirm the page isn't also statically rendered/ISR — serving the same cached card to all users is a Step 4 freshness mismatch.\n- If there's a `Date.now()`/locale/timezone format anywhere in the card's render path, it's a Step 5 hydration-mismatch source; flag it regardless of visual impact.\n\n**Report line:** `app/dashboard/page.tsx:1 — 'use client' at page root drags entire static dashboard subtree client-side for one tooltip card. Move directive to extracted StatCard.tsx leaf; pass serialized props from server parent.`\n\nNo files were modified; this is a review report only.",
1394
+ "outputSha256": "45ff49b3fb376cd5e252a60011837ec666f01b751a4a02e1e12e0043ee9f2dba",
1395
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1396
+ "deterministic": [
1397
+ true
1398
+ ],
1399
+ "judge": {
1400
+ "verdict": "pass",
1401
+ "reason": "Answer states page-level 'use client' is too broad ('entire page subtree... pulled into the client bundle... for the sake of one stat card's hover tooltip') and concretely recommends extracting the stat card into its own file 'app/dashboard/StatCard.tsx' with 'use client' there only, while page.tsx 'stays a Server Component with no directive'. It rejects the 'simpler as one unit' rationalization, so no fail criterion holds."
1402
+ },
1403
+ "passed": true
1404
+ },
1405
+ {
1406
+ "output": "The review should flag this as a boundary-placement finding, not wave it through because \"only one small piece needs it.\"\n\n**Finding:** `app/dashboard/page.tsx:1` — `'use client'` is placed on the whole page to support one small stat card's hover tooltip (local state).\n\n**Why it's wrong:** A page-level `'use client'` directive doesn't just mark the page as needing a client boundary — it makes the page component *and its entire import subtree* client-side. Every static server-rendered element on that dashboard (the rest of the page, per the diff description) now ships its JS, framework runtime, and any props data to the browser as part of the client bundle. The cost of one tooltip becomes the cost of the whole page being client-rendered instead of server-rendered. This is the exact \"smallest boundary possible\" failure from Step 2 of the review workflow: the directive should sit on the component that actually needs browser state/event handling, not on a shared page or ancestor.\n\n**Concrete fix:** Extract the stat card (or the tooltip-bearing part of it) into its own file, e.g. `app/dashboard/StatCard.tsx`, put `'use client'` at the top of *that* file, and import it into the still-server-rendered `page.tsx`. The page keeps `page.tsx` as a Server Component; only `StatCard` crosses the boundary. This preserves the static server-render for everything else.\n\n**Secondary check worth noting in the review:** since the card now becomes a Client Component, confirm the boundary doesn't force passing server-only data (session/cookies, DB reads, API keys) down as props. If the stat value is computed server-side, pass only the rendered value the tooltip needs, not the raw server object. Also verify — per the hydration-mismatch check — that the tooltip's tooltip content doesn't itself render `Date.now()`/`Math.random()`/locale-dependent formatting, which would reintroduce a mismatch even after the boundary is fixed.\n\n**Report format:** state the finding at `app/dashboard/page.tsx:1` with the above rationale and the concrete extract-to-`StatCard.tsx` fix; do not edit the file — this skill reports only. Note that a page-level `'use client'` is not a style nit; it is a real bundle and rendering-mode cost and should be raised as such.\n\nRationalization to reject while writing the review: \"'use client' is on the page but it's simpler to review as one unit\" — the whole point of the check is that this placement ships the entire subtree to the browser, so it's the finding to catch, not to wave through for simplicity.",
1407
+ "outputSha256": "e9ecb654298efae4ff44b42237dcdc7fd476e50bb4a28979af9a37c7e2d2d711",
1408
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1409
+ "deterministic": [
1410
+ true
1411
+ ],
1412
+ "judge": {
1413
+ "verdict": "pass",
1414
+ "reason": "Answer states page-level 'use client' is too broad ('makes the page component and its entire import subtree client-side', 'cost of one tooltip becomes the cost of the whole page') and concretely recommends extracting the stat card into app/dashboard/StatCard.tsx with 'use client' there, keeping page.tsx a Server Component. It explicitly rejects the 'simpler as one unit' rationalization, so no fail criterion holds."
1415
+ },
1416
+ "passed": true
1417
+ },
1418
+ {
1419
+ "output": "This is the exact boundary-placement finding from Step 2 and the first Red Flags row: `'use client'` is on the page, an ancestor of the component that actually needs it, so the entire static subtree gets shipped to the browser as client JS for the sake of one tooltip's local state.\n\n**Finding:** `app/dashboard/page.tsx:1` — `'use client'` is on the page, but only one small stat card needs browser state (the hover tooltip). Everything else on the page is static server-rendered content, so this silently converts the whole page — and every component it renders — into a Client Component, pulling it all into the client bundle and losing the Server Component benefits (no server-only data access in that subtree, larger client payload).\n\n**Fix:** Move the interactivity down to the smallest component that needs it. Extract the stat card into its own file, e.g. `app/dashboard/StatCard.tsx`, put `'use client'` at the top of *that file only*, and keep `app/dashboard/page.tsx` as a Server Component that imports and renders `<StatCard />`. The page keeps server rendering for everything else; only the card crosses the boundary.\n\n**Additional checks while you're in there (so this doesn't slip through as a one-line nit):**\n\n- Confirm the extracted `StatCard` doesn't pull a server-only import into its client import chain — if it displays data fetched on the server, pass that data down as props from the Server Component rather than having the card fetch or import it itself (Step 2, third bullet).\n- If any server-only value (session, DB-derived field) currently flows into the page and would now sit inside a Client Component, verify the page only passes what the card actually needs to render, not the raw source object.\n- Re-check rendering mode (Step 4): if this dashboard was previously static/ISR and the tooltip was the only reason for the directive, moving `'use client'` down should let the page stay static — confirm no route-level cache/revalidate config was changed to accommodate the old page-level directive.\n\n**Report line:** `app/dashboard/page.tsx:1` — `'use client'` placed on the page for a single interactive stat card; hoist it to a new client `StatCard` component and keep the page as a Server Component. This is a boundary-placement finding, not a style nit: it's the placement mistake Step 2 exists to catch.",
1420
+ "outputSha256": "9f88343b39090f5d9adcc5dfcd658728f8455f87f1eb003ffcd11d5955697828",
1421
+ "promptSha256": "9ed883aaf78dd9290b79838a55ab630974bf7388d54e11e4c3a097d610f5c2d4",
1422
+ "deterministic": [
1423
+ true
1424
+ ],
1425
+ "judge": {
1426
+ "verdict": "pass",
1427
+ "reason": "Finding states 'use client' is on the page but only one small stat card needs browser state, converting whole page to Client Component (criterion 1). Fix concretely extracts stat card into app/dashboard/StatCard.tsx with 'use client' at top of that file only, keeping page.tsx a Server Component importing <StatCard /> (criterion 2). No approval of page-level directive."
1428
+ },
1429
+ "passed": true
1430
+ }
1431
+ ]
1432
+ }
1433
+ ],
1434
+ "verdict": "fail",
1435
+ "scope": "bundled",
1436
+ "skillDigest": "f1b622a4d3e1b9ae4b488b3e3581bd21a369a1986e0873edc0f351c3eddce18a",
1437
+ "catalogDigest": "139cc7e7c63f9a3fcb3560551b7740c5642c828db2570e2f40728089218cc6d4",
1438
+ "judgePromptVersion": "2026-09-25.1",
1439
+ "runner": "deepseek",
1440
+ "model": "deepseek-chat",
1441
+ "runnerPromptVersion": "2026-09-25.1",
1442
+ "recordedAt": "2026-09-25T15:02:10.208Z",
1443
+ "judge": "deepseek",
1444
+ "judgeModel": "deepseek-chat"
1445
+ },
1446
+ {
1447
+ "schemaVersion": "1.0.0",
1448
+ "skillId": "nextjs-nuxt/nextjs-nuxt-build-fix",
1449
+ "strictness": "high",
1450
+ "trials": 10,
1451
+ "triggerAccuracy": {
1452
+ "truePositive": 7,
1453
+ "falsePositive": 1,
1454
+ "positives": 7,
1455
+ "negatives": 6
1456
+ },
1457
+ "evidence": "authored",
1458
+ "scenarios": [
1459
+ {
1460
+ "id": "trigger-positive-1",
1461
+ "kind": "trigger-positive",
1462
+ "prompt": "This Server Component in my Next.js app imports useState, and the build keeps failing because of it, what's the actual fix?",
1463
+ "strictness": "high",
1464
+ "trials": 1,
1465
+ "passes": 1,
1466
+ "passRate": 1,
1467
+ "passAtK": 1,
1468
+ "grader": "trigger-rank-fork-family",
1469
+ "status": "ran",
1470
+ "deterministic": true
1471
+ },
1472
+ {
1473
+ "id": "trigger-positive-2",
1474
+ "kind": "trigger-positive",
1475
+ "prompt": "Every time this Next.js page renders, the console throws a hydration mismatch error, and I need to actually fix the root cause instead of suppressing it.",
1476
+ "strictness": "high",
1477
+ "trials": 1,
1478
+ "passes": 1,
1479
+ "passRate": 1,
1480
+ "passAtK": 1,
1481
+ "grader": "trigger-rank-fork-family",
1482
+ "status": "ran",
1483
+ "deterministic": true
1484
+ },
1485
+ {
1486
+ "id": "trigger-positive-3",
1487
+ "kind": "trigger-positive",
1488
+ "prompt": "Running nuxi typecheck, I keep getting a failure because it can't resolve this auto-imported composable, how do I fix the build?",
1489
+ "strictness": "high",
1490
+ "trials": 1,
1491
+ "passes": 1,
1492
+ "passRate": 1,
1493
+ "passAtK": 1,
1494
+ "grader": "trigger-rank-fork-family",
1495
+ "status": "ran",
1496
+ "deterministic": true
1497
+ },
1498
+ {
1499
+ "id": "trigger-positive-4",
1500
+ "kind": "trigger-positive",
1501
+ "prompt": "This Next.js route.ts file has an invalid export and won't build",
1502
+ "strictness": "high",
1503
+ "trials": 1,
1504
+ "passes": 1,
1505
+ "passRate": 1,
1506
+ "passAtK": 1,
1507
+ "grader": "trigger-rank-fork-family",
1508
+ "status": "ran",
1509
+ "deterministic": true
1510
+ },
1511
+ {
1512
+ "id": "trigger-positive-5",
1513
+ "kind": "trigger-positive",
1514
+ "prompt": "Fix this nuxt build error about a composable being called outside setup",
1515
+ "strictness": "high",
1516
+ "trials": 1,
1517
+ "passes": 1,
1518
+ "passRate": 1,
1519
+ "passAtK": 1,
1520
+ "grader": "trigger-rank-fork-family",
1521
+ "status": "ran",
1522
+ "deterministic": true
1523
+ },
1524
+ {
1525
+ "id": "trigger-positive-6",
1526
+ "kind": "trigger-positive",
1527
+ "prompt": "My next build is failing because a Server Component in this file imports a browser-only API, what's the correct fix?",
1528
+ "strictness": "high",
1529
+ "trials": 1,
1530
+ "passes": 1,
1531
+ "passRate": 1,
1532
+ "passAtK": 1,
1533
+ "grader": "trigger-rank-fork-family",
1534
+ "status": "ran",
1535
+ "deterministic": true
1536
+ },
1537
+ {
1538
+ "id": "trigger-positive-7",
1539
+ "kind": "trigger-positive",
1540
+ "prompt": "Nuxt build can't find this component even though it's in the components folder",
1541
+ "strictness": "high",
1542
+ "trials": 1,
1543
+ "passes": 1,
1544
+ "passRate": 1,
1545
+ "passAtK": 1,
1546
+ "grader": "trigger-rank-fork-family",
1547
+ "status": "ran",
1548
+ "deterministic": true
1549
+ },
1550
+ {
1551
+ "id": "trigger-negative-1",
1552
+ "kind": "trigger-negative",
1553
+ "prompt": "Add a new Next.js server action for creating an order",
1554
+ "strictness": "high",
1555
+ "trials": 1,
1556
+ "passes": 0,
1557
+ "passRate": 0,
1558
+ "passAtK": 0,
1559
+ "grader": "trigger-rank-fork-family",
1560
+ "status": "ran",
1561
+ "deterministic": true
1562
+ },
1563
+ {
1564
+ "id": "trigger-negative-2",
1565
+ "kind": "trigger-negative",
1566
+ "prompt": "Review this Next.js server action for missing auth checks",
1567
+ "strictness": "high",
1568
+ "trials": 1,
1569
+ "passes": 1,
1570
+ "passRate": 1,
1571
+ "passAtK": 1,
1572
+ "grader": "trigger-rank-fork-family",
1573
+ "status": "ran",
1574
+ "deterministic": true
1575
+ },
1576
+ {
1577
+ "id": "trigger-negative-3",
1578
+ "kind": "trigger-negative",
1579
+ "prompt": "Write a test for this Nuxt composable",
1580
+ "strictness": "high",
1581
+ "trials": 1,
1582
+ "passes": 1,
1583
+ "passRate": 1,
1584
+ "passAtK": 1,
1585
+ "grader": "trigger-rank-fork-family",
1586
+ "status": "ran",
1587
+ "deterministic": true
1588
+ },
1589
+ {
1590
+ "id": "trigger-negative-4",
1591
+ "kind": "trigger-negative",
1592
+ "prompt": "Migrate this Nuxt 2 project to Nuxt 3",
1593
+ "strictness": "high",
1594
+ "trials": 1,
1595
+ "passes": 1,
1596
+ "passRate": 1,
1597
+ "passAtK": 1,
1598
+ "grader": "trigger-rank-fork-family",
1599
+ "status": "ran",
1600
+ "deterministic": true
1601
+ },
1602
+ {
1603
+ "id": "trigger-negative-5",
1604
+ "kind": "trigger-negative",
1605
+ "prompt": "Fix this Vite build error in a plain Vue component with no Nuxt involved",
1606
+ "strictness": "high",
1607
+ "trials": 1,
1608
+ "passes": 1,
1609
+ "passRate": 1,
1610
+ "passAtK": 1,
1611
+ "grader": "trigger-rank-fork-family",
1612
+ "status": "ran",
1613
+ "deterministic": true
1614
+ },
1615
+ {
1616
+ "id": "trigger-negative-6",
1617
+ "kind": "trigger-negative",
1618
+ "prompt": "Fix this generic tsc module resolution error in a Node script",
1619
+ "strictness": "high",
1620
+ "trials": 1,
1621
+ "passes": 1,
1622
+ "passRate": 1,
1623
+ "passAtK": 1,
1624
+ "grader": "trigger-rank-fork-family",
1625
+ "status": "ran",
1626
+ "deterministic": true
1627
+ },
1628
+ {
1629
+ "id": "no-suppress-hydration-warning",
1630
+ "kind": "behavior",
1631
+ "prompt": "My Next.js page shows a hydration mismatch warning in the console because it renders new Date().toLocaleTimeString() directly in the component. What's the right fix?",
1632
+ "strictness": "high",
1633
+ "trials": 10,
1634
+ "passes": 10,
1635
+ "passRate": 1,
1636
+ "passAtK": 1,
1637
+ "grader": "regex+judge",
1638
+ "status": "ran",
1639
+ "trialRecords": [
1640
+ {
1641
+ "output": "The right fix is to make that value deterministic between server and client, or to only render it on the client — not to silence the warning.\n\n**Why it mismatches:** `new Date().toLocaleTimeString()` is evaluated during server render (at server time, in the server's timezone/locale) and again during the client's first render (at a slightly different time, potentially a different timezone/locale). The two strings differ, so React's hydration comparison fails.\n\n**Smallest correct fixes, pick the one matching intent:**\n\n1. **If you just need \"now\" to display live and server-side is meaningless** — render it only after mount, so the server and the client's first (hydrating) render agree on an empty/placeholder state:\n\n```tsx\n'use client';\nimport { useState, useEffect } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n return <span>{time ?? ''}</span>;\n}\n```\n\nThe first client render outputs `''`, matching the server, so hydration is clean; the real value appears on the post-mount re-render.\n\n2. **If the value is genuinely available at request time and should be server-rendered** — compute it once on the server (e.g. in a Server Component or a Route Handler) and pass the resulting *string* down as a prop to a presentational component. Don't recompute it on the client.\n\n3. **If a stable timestamp is fine (e.g. \"generated at\" label)** — pass an explicit, fixed `timeZone`/`locale` and compute the string once at a single source, then render that same string on both sides.\n\n**Do not do any of these:**\n- `suppressHydrationWarning` on the element — hides the mismatch instead of fixing it.\n- `<ClientOnly>` wrapping the whole subtree just to dodge the mismatch.\n- `export const dynamic = 'force-dynamic'` or disabling SSR as a blanket fix.\n\nIn short: the mismatch is caused by a nondeterministic value being rendered during SSR and again during hydration. Fix it by rendering that value only after mount (fix 1), or by computing it once on the server and passing it down as data (fix 2).",
1642
+ "outputSha256": "91472c43393a4d6800f2b6dc3af0343bea5781ef12eaba38f74dc054824d4058",
1643
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1644
+ "deterministic": [
1645
+ true
1646
+ ],
1647
+ "judge": {
1648
+ "verdict": "pass",
1649
+ "reason": "Answer names concrete fixes: fix 1 shows mounted-state pattern with useState(null) + useEffect setting time after mount, rendering '' on first client render to match server; fix 2 passes server-computed string down as prop. It explicitly warns against suppressHydrationWarning as hiding the mismatch, not recommending it."
1650
+ },
1651
+ "passed": true
1652
+ },
1653
+ {
1654
+ "output": "The right fix is to make that value stop being different on the server pass vs. the client pass, rather than silencing the warning.\n\nWhat's actually happening: `new Date().toLocaleTimeString()` is evaluated twice — once on the server (producing the server's clock/locale) and again on the client during hydration (producing the browser's clock/locale a moment later). Those two strings differ, so React's hydration sees text that doesn't match what it expected and warns. The value is inherently nondeterministic across the two renders, and the only correct fix is to render a value that the server and the first client render agree on.\n\nThe smallest correct fix: don't render the live time during the server render at all. Compute it after mount, when the client is the only renderer.\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n if (time === null) return <time aria-hidden=\"true\" />;\n return <time>{time}</time>;\n}\n```\n\nThe key point: the first client render (the hydration pass) returns the same thing as the server render (`null` → empty `<time>`), so nothing mismatches. The real time appears only after `useEffect` runs, which never happens on the server. If you'd rather show a static placeholder instead of an empty element, render that same placeholder on both sides — the placeholder itself must be deterministic, but a static string like `\"Loading…\"` or `\"--:--\"` is fine.\n\nIf the timestamp is genuinely fixed and doesn't need to tick, the cleaner fix is to compute it once on the server and pass it down as a prop/serialized value, so both renders use the identical string. Use this only when the value isn't supposed to change per-second; for a live clock, the mount-effect approach above is the right one.\n\nWhat to avoid:\n\n- `suppressHydrationWarning` — this hides the warning but doesn't fix the nondeterminism; the DOM and the client's expectation still diverge, and you've just lost the signal that would tell you when the mismatch grows.\n- `<ClientOnly>` / `ssr: false` on the page/route — these turn off server rendering for everything on the page to work around one timestamp, which is a much bigger behavioral change than the problem warrants. (A `<ClientOnly>` wrapper narrowly around just this clock is defensible in Nuxt, but the mount-effect pattern above is still the more precise fix and works identically in Next.js.)\n\nOne more thing: the same bug class covers `Math.random()`, `typeof window` branching during render, `Date.now()` in the markup, and locale-dependent formatting (`.toLocaleDateString()`, `Intl.NumberFormat`) — audit for those in the same component while you're in there, since they'll warn for the same reason.",
1655
+ "outputSha256": "77391d0164abb926d01c7e1eb57c3f0b50bf9ccd8c19021366ed70c870012dbb",
1656
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1657
+ "deterministic": [
1658
+ true
1659
+ ],
1660
+ "judge": {
1661
+ "verdict": "pass",
1662
+ "reason": "Answer names the concrete fix: mounted-state/effect pattern with full code — useState<string|null>(null), useEffect calling setTime(new Date().toLocaleTimeString()), returning empty <time> until mounted, so hydration matches. Also names the server-computed prop alternative. It explicitly warns against suppressHydrationWarning as hiding the warning, not recommending it. Both pass criteria met, no fail criterion holds."
1663
+ },
1664
+ "passed": true
1665
+ },
1666
+ {
1667
+ "output": "The right fix is to make the rendered value deterministic across server and client — a timestamp is one of the classic nondeterministic-render sources (along with `Math.random()`, `Date.now()`, and locale-dependent formatting), so the server HTML will essentially never match the client's first render.\n\nDo this in two parts:\n\n1. **Render a stable value on the server and the first client pass.** Compute the formatted time once, in a place both environments agree on, and pass it down as a prop. For example, format the time on the server (Server Component / route handler / loader) and render the resulting string:\n\n ```tsx\n // Server Component\n export default function Page() {\n const formatted = new Date().toLocaleTimeString();\n return <Clock initial={formatted} />;\n }\n\n // Client Component\n 'use client';\n export function Clock({ initial }: { initial: string }) {\n return <time>{initial}</time>;\n }\n ```\n\n Now the server HTML and the client's first render both use the same string, so they match.\n\n2. **If you actually want it to update live on the client**, add a mount effect that swaps in the client-only value after hydration:\n\n ```tsx\n 'use client';\n import { useEffect, useState } from 'react';\n\n export function Clock({ initial }: { initial: string }) {\n const [time, setTime] = useState(initial);\n useEffect(() => {\n const id = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);\n return () => clearInterval(id);\n }, []);\n return <time>{time}</time>;\n }\n ```\n\n The first render still uses `initial` (matching the server), and only the post-hydration update carries the live value — no mismatch.\n\n**What not to do:** don't slap `suppressHydrationWarning` on the element. That only silences the warning while leaving the actual nondeterministic render in place, and it can hide a real mismatch that grows over time. Likewise, don't fall back to `dynamic(() => ..., { ssr: false })` on the whole component just to dodge the warning — that throws away server rendering for content that never needed it. Fix the render, not the warning.\n\nSo: is the clock meant to be a fixed snapshot (just use approach 1) or a live-updating clock (approach 1 + the mount-effect swap in approach 2)? That choice decides how much of the above you need.",
1668
+ "outputSha256": "567e51efb4e9ce5100bc92b0ee0ce024a95466d5b4dafd12ded913b733c40f2f",
1669
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1670
+ "deterministic": [
1671
+ true
1672
+ ],
1673
+ "judge": {
1674
+ "verdict": "pass",
1675
+ "reason": "Names concrete fixes: passing a server-computed formatted string as an `initial` prop (with code showing `<Clock initial={formatted} />`), and a mount-effect pattern using useState(initial) + useEffect setInterval to swap in the live value post-hydration. Both shown in code. Explicitly warns against suppressHydrationWarning as the fix, which is allowed."
1676
+ },
1677
+ "passed": true
1678
+ },
1679
+ {
1680
+ "output": "# Fixing the `new Date().toLocaleTimeString()` hydration mismatch\n\nThe root cause: `new Date().toLocaleTimeString()` produces *different output on the server and on the client*, because the server renders once in its own locale/timezone and the client renders again in the browser's locale/timezone. React's first client render differs from the server HTML, so it throws a hydration mismatch.\n\nThe right fix is to make the value deterministic and identical across server and client — not to silence the warning. Pick the approach that matches what you actually want displayed:\n\n## Option A — You want a fixed/static timestamp (no live ticking)\n\nCompute the value once on the server and pass it down as a prop, so both renders use the same string:\n\n```tsx\n// app/page.tsx (Server Component — no 'use client')\nexport default function Page() {\n const renderedAt = new Date().toLocaleTimeString();\n return <ClockLabel label={renderedAt} />;\n}\n```\n\n```tsx\n// ClockLabel.tsx\n'use client';\n\nexport function ClockLabel({ label }: { label: string }) {\n return <span>{label}</span>;\n}\n```\n\nBoth server HTML and first client render use the identical `label` string → no mismatch.\n\n## Option B — You want the *browser's* local time (a live clock)\n\nThat value legitimately can't exist at server-render time. Don't compute it during render; compute it inside a mount effect, so the first client render matches the server (which renders nothing/placeholder) and the real value appears after mount:\n\n```tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n // Server render and first client render both hit this branch:\n return <span>{time ?? '—'}</span>;\n}\n```\n\nBecause `time` is `null` on both the server render and the client's initial render, they match. The effect then updates it client-side. This is the standard fix and is exactly what must **not** be replaced with `<ClientOnly>`/`ssr: false` or `suppressHydrationWarning`.\n\n## What NOT to do\n\n- **Don't** add `suppressHydrationWarning` to the element. That hides the symptom and leaves the underlying nondeterministic render in place.\n- **Don't** wrap the whole page in a client boundary or set `ssr: false` on the route just to shut the warning up — that disables server rendering for content that didn't need it.\n- **Don't** mark the whole layout `'use client'` to make the error \"go away for sure.\"\n\n## Why this addresses the root cause\n\n`toLocaleTimeString()` depends on the ambient locale and timezone, which differ between the Node server and the browser. Any of the following in render will cause the same class of mismatch: `Date.now()`, `Math.random()`, `typeof window` branching, or locale-dependent `Intl` formatting. The fix is always to make the rendered output deterministic — either compute it once on the server (Option A) or defer the client-only-varying value to a mount effect (Option B).\n\n## Verification\n\nAfter the change, re-run the command that surfaced the mismatch:\n\n```bash\nnpx next build\n```\n\nand confirm it exits 0 with no hydration warnings in the browser console on first load. The root cause here is the ambient-locale/timezone-dependent call during render — not a formatting bug — so the fix must eliminate that nondeterminism rather than suppress the warning.",
1681
+ "outputSha256": "6cb8779897809760dd60964c5e66f8aa60e8e91ca62028eac010cdab2d413939",
1682
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1683
+ "deterministic": [
1684
+ true
1685
+ ],
1686
+ "judge": {
1687
+ "verdict": "pass",
1688
+ "reason": "Option A passes a server-computed timestamp as a stable prop (renderedAt -> ClockLabel label). Option B shows the mounted-flag/effect pattern: useState(null) + useEffect setting time after mount, with code. suppressHydrationWarning is only warned against, not recommended. Both pass criteria met."
1689
+ },
1690
+ "passed": true
1691
+ },
1692
+ {
1693
+ "output": "The right fix is to make that value deterministic across server and client instead of rendering a clock value during the render pass.\n\n`new Date().toLocaleTimeString()` produces a different string on the server (render time) than on the client (hydration time), so React's first client render can't match the server HTML — hence the warning. Suppressing it (`suppressHydrationWarning`) or wrapping the component in `<ClientOnly>`/`ssr: false` would just hide it.\n\nTwo correct approaches:\n\n1. **Pass a fixed value down.** Compute the timestamp on the server (or once, at the top of the page), and render that exact string:\n\n ```tsx\n // server component or page\n const serverTime = new Date().toLocaleTimeString();\n return <Clock initialTime={serverTime} />;\n ```\n\n The client's first render uses `initialTime`, so server and client HTML match.\n\n2. **Render it only after mount.** If it genuinely must be \"live\" client time, gate it behind an effect so the first client render matches the server:\n\n ```tsx\n 'use client';\n import { useEffect, useState } from 'react';\n\n export function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n return <span>{time ?? '--:--:--'}</span>; // server + first client render match\n }\n ```\n\n The placeholder (`'--:--:--'` or `null`) is what renders on the server and on the very first client pass, so they agree; the real time only appears after mount.\n\nNote that both fixes also require a deterministic *locale*. If the server and client could be in different locales/timezones, `toLocaleTimeString()` (and any locale-dependent formatter) will still mismatch even with a fixed timestamp — pin the locale explicitly (`toLocaleTimeString('en-US')`) and make sure the timezone is consistent, or compute the formatted string once on the server and pass the finished string down.\n\nPrefer option 1 if the value can be server-rendered at all; use option 2 only when the value is intrinsically client-live. Either way, don't reach for `suppressHydrationWarning`, `<ClientOnly>`, or `ssr: false`.",
1694
+ "outputSha256": "be32ad1a1bc704a2c0390a33ab47fdabbb348f574a1797ff5132c212510eb60c",
1695
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1696
+ "deterministic": [
1697
+ true
1698
+ ],
1699
+ "judge": {
1700
+ "verdict": "pass",
1701
+ "reason": "Names two concrete fixes: passing a server-computed timestamp as a prop (shows `const serverTime = new Date().toLocaleTimeString(); return <Clock initialTime={serverTime} />`) and the mounted/effect pattern (shows useEffect with useState(null) and `time ?? '--:--:--'` placeholder). Explicitly rejects suppressHydrationWarning as merely hiding the warning, so no fail criterion holds."
1702
+ },
1703
+ "passed": true
1704
+ },
1705
+ {
1706
+ "output": "The warning is real: `new Date().toLocaleTimeString()` produces a different string on the server (render time) than on the client (hydration time), so React sees mismatched text and warns.\n\nThe right fix is to make the value deterministic across server and client. Two correct options, depending on what you actually want to show:\n\n**Option A — gate the time behind a mount effect (if it must tick / be \"now\")**\n\nExtract the clock into its own small Client Component and only render the time after mount:\n\n```tsx\n'use client';\nimport { useState, useEffect } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n // server + first client render both produce null -> identical,\n // so no mismatch; the real time appears on the next client render.\n return <span>{time ?? '--:--:--'}</span>;\n}\n```\n\nKeep the `'use client'` on this small component, not on the whole page.\n\n**Option B — compute the time once on the server and pass it down (if it's a fixed \"generated at\" timestamp)**\n\nIf the time doesn't need to live-update, compute it in the Server Component and pass it as a prop. Because the same value is used for the server HTML and the client's initial render, they match.\n\n```tsx\n// Server Component (page.tsx)\nexport default function Page() {\n const generatedAt = new Date().toLocaleTimeString();\n return <p>Generated at {generatedAt}</p>;\n}\n```\n\nThe key property in both cases: the server render and the client's first render must produce the same output. Option B does that by fixing the value; Option A does that by rendering a stable placeholder until the client has mounted.\n\n**What not to do:** don't slap `suppressHydrationWarning` on the element, and don't wrap the page in a blanket client-only escape hatch. That just hides the symptom — the nondeterministic render is still there, and any future mismatch on that subtree stays silent. Fixing the source (deterministic value, or mount-gated render) is what actually removes the warning.\n\nIf you're also seeing a specific text/attribute in the warning message, that's worth confirming, since a mismatch isn't always the timestamp — a `typeof window` branch or a locale-dependent format elsewhere in the same subtree produces the same warning.",
1707
+ "outputSha256": "a0b09fd0aa42889752c754c40c73ea0a0ec86e6fc208002a78ec9d6234cfce05",
1708
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1709
+ "deterministic": [
1710
+ true
1711
+ ],
1712
+ "judge": {
1713
+ "verdict": "pass",
1714
+ "reason": "Option A shows the mounted-flag/effect pattern concretely: useState(null) + useEffect setting time after mount, with '--:--:--' placeholder so server and first client render match. Option B passes a server-computed generatedAt prop. Both name the specific code change. suppressHydrationWarning is only mentioned to warn against it, which is not a failure."
1715
+ },
1716
+ "passed": true
1717
+ },
1718
+ {
1719
+ "output": "The right fix is to make that value deterministic between server render and client's first render — don't blanket-suppress the warning.\n\n**Why it's a mismatch:** the server renders the page at request time and computes `new Date().toLocaleTimeString()` as, say, `\"14:03:27\"`. That HTML ships to the browser. When React hydrates, the client component runs `new Date().toLocaleTimeString()` again — now it's `\"14:03:29\"`. React compares the two and warns, because the server HTML and the client's first render disagree. The value is inherently nondeterministic across the two renders, which is exactly what hydration forbids.\n\n**The correct fix depends on what the time is for:**\n\n1. **If the timestamp is just \"current time, only meaningful on the client\"** — render a stable placeholder on the server and fill in the real value after mount:\n\n ```tsx\n 'use client';\n import { useState, useEffect } from 'react';\n\n export function Clock() {\n const [time, setTime] = useState<string | null>(null);\n useEffect(() => {\n setTime(new Date().toLocaleTimeString());\n }, []);\n return <time>{time ?? '--:--:--'}</time>;\n }\n ```\n\n The server and the client's first render both produce `\"--:--:--\"`, so they match; the real time appears on the second (post-mount) render, which React is allowed to do. If the value should tick, move it into an interval inside the same effect and clear it on cleanup.\n\n2. **If the timestamp represents a fixed moment** (a record's `createdAt`, a build time, a server-fetched event time) — compute it once on the server, pass it down as a prop, and format it with a fixed locale/timezone so both renders agree:\n\n ```tsx\n // Server Component\n const createdAt = record.createdAt; // already a fixed value\n return <CreatedAt iso={createdAt.toISOString()} />;\n\n // Client Component\n 'use client';\n export function CreatedAt({ iso }: { iso: string }) {\n return <time dateTime={iso}>{new Date(iso).toLocaleTimeString('en-US', { timeZone: 'UTC' })}</time>;\n }\n ```\n\n The nondeterminism here is usually the *implicit* locale/timezone — the server's TZ isn't the browser's TZ, so pin both the locale and the `timeZone`. Same ISO string in, same string out, on both sides.\n\n3. **If the clock is genuinely a client-only widget with no server value** — the mount-effect pattern from (1) is still the right shape. Do **not** reach for `<ClientOnly>` or `ssr: false` on the route; that turns off server rendering for the whole subtree to hide a mismatch caused by one value.\n\n**What not to do:** don't add `suppressHydrationWarning` to the element. It silences the warning, but the underlying render is still nondeterministic — the server HTML stays wrong for anything that reads it (search engines, no-JS clients), and the moment the mismatched value grows to affect real layout or text, you've lost the signal that would have caught it.\n\n**Any nondeterministic source trips the same wire** — `Math.random()`, `typeof window` branching, `Intl` output without a pinned locale, \"time since\" strings computed during render — so if the console still shows a mismatch after fixing the clock, look for those next.\n\nTo verify: rebuild and load the page; the console should be clean, and the rendered time should match server HTML on first paint, then update to the live value after mount.",
1720
+ "outputSha256": "acc4c54a200db923f9de7d8608bc392be00396f2f4d28d1deb4810f78185d527",
1721
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1722
+ "deterministic": [
1723
+ true
1724
+ ],
1725
+ "judge": {
1726
+ "verdict": "pass",
1727
+ "reason": "Names concrete fixes: mounted-state pattern with useState(null)+useEffect setTime(new Date().toLocaleTimeString()) and placeholder '--:--:--' (code shown), plus server-computed timestamp passed as iso prop to CreatedAt. Explicitly warns against suppressHydrationWarning as the fix, not recommending it. Both pass criteria met."
1728
+ },
1729
+ "passed": true
1730
+ },
1731
+ {
1732
+ "output": "The right fix is to make the rendered output **deterministic** so the server render and the client's first render agree, then let the real current time appear only after hydration. Concretely, in order of preference:\n\n**Option A — render nothing/an empty placeholder on the server, fill in after mount (recommended when you truly want the client's live clock):**\n\n```tsx\n'use client';\nimport { useEffect, useState } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n // Server and first client render both produce this — no mismatch.\n return <span suppressHydrationWarning={false}>{time ?? '--:--:--'}</span>;\n}\n```\n\nThe server emits `'--:--:--'`; the client's first render also emits `'--:--:--'`, so hydration matches exactly. The actual time only appears inside `useEffect`, which never runs on the server or during the first client render.\n\n**Option B — compute the value on the server and pass it down as a prop**, if a server-rendered timestamp is acceptable:\n\n```tsx\n// server component\nexport default function Page() {\n const renderedAt = new Date().toLocaleTimeString();\n return <Clock initialTime={renderedAt} />;\n}\n```\n\nHere both server and client render the *same* string, and any ticking updates happen in a client effect. This only works if you don't need it to stay live/accurate to the second.\n\n**Option C — if a minute-level timestamp is enough and you don't need it live**, just compute it once on the server and render it as static text with no client-side clock at all.\n\n**What not to do:**\n- Don't slap `suppressHydrationWarning` on the element. That silences React's warning but the server and client DOM still disagree — you've hidden a real bug, and it can silently grow.\n- Don't move to a `<ClientOnly>` wrapper or set `ssr: false` just for this. That disables server rendering for the whole subtree instead of fixing the one nondeterministic value.\n\nThe underlying cause is always the same: `new Date()` (like `Date.now()`, `Math.random()`, or `typeof window` branching) produces different output on the server than on the client. Fix it by giving the first render a stable value and deferring the varying value to a mount effect.",
1733
+ "outputSha256": "b036cf0fda3fbbacc20d6a1d5ee64ac6f61373e878a3d01f95b7a06ce353fbb9",
1734
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1735
+ "deterministic": [
1736
+ true
1737
+ ],
1738
+ "judge": {
1739
+ "verdict": "pass",
1740
+ "reason": "Option A shows the mounted-flag/effect pattern concretely: useState(null) + useEffect setting time after mount, rendering '--:--:--' on server and first client render. Option B passes a server-computed timestamp as a prop (initialTime). Both name specific code. suppressHydrationWarning is only mentioned to warn against it, not recommended."
1741
+ },
1742
+ "passed": true
1743
+ },
1744
+ {
1745
+ "output": "The right fix is to make the rendered value deterministic and then update it after mount — not to silence the warning.\n\n`new Date().toLocaleTimeString()` produces a different string on the server (render time) than on the client (hydrate time), which is exactly what causes the mismatch. The fix:\n\n1. Render a stable placeholder on both server and client for the first pass, then\n2. Compute the real time inside an effect that only runs on the client, and\n3. Store it in state so the second render updates it.\n\nA minimal pattern:\n\n```tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\n\nexport default function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n setTime(new Date().toLocaleTimeString());\n // if it should keep ticking, add an interval here\n }, []);\n\n return <span>{time ?? '--:--:--'}</span>;\n}\n```\n\nKey points:\n\n- `useState(null)` + `useEffect` guarantees the server output and the client's first render match (both show the placeholder), so hydration succeeds.\n- The real time only appears after mount, which is a client-only concern anyway.\n- If this component is not already interactive, this is the smallest file that needs `'use client'` — don't push it up to a shared layout.\n\nWhat not to do:\n\n- Don't add `suppressHydrationWarning` on the element to hide the warning.\n- Don't wrap it in a blanket `<ClientOnly>` / set `ssr: false` on the route just to dodge the mismatch.\n- Don't fix it by tweaking text only — the underlying nondeterminism (calling `Date` during render) must be removed.\n\nThe same shape applies to any other nondeterministic render source: `Math.random()`, `typeof window` branching, or locale-dependent formatting. Compute the varying value once on the client after mount, or compute it once on the server and pass the resulting value down as a prop — never call the nondeterministic function directly in render on both sides.",
1746
+ "outputSha256": "c1fa4f1592b346252577d8e0ba34b6c333c47ff0f7a275cd9ddec08d51e85691",
1747
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1748
+ "deterministic": [
1749
+ true
1750
+ ],
1751
+ "judge": {
1752
+ "verdict": "pass",
1753
+ "reason": "Answer names the concrete fix: useState(null) + useEffect setting time after mount, with full code showing the mounted/effect pattern and placeholder '--:--:--'. It also mentions passing a server-computed value down as a prop. It explicitly warns against suppressHydrationWarning rather than recommending it, so no fail criterion holds."
1754
+ },
1755
+ "passed": true
1756
+ },
1757
+ {
1758
+ "output": "The right fix is to render that timestamp deterministically, or to make it a client-only update after mount — not to silence the warning. `new Date().toLocaleTimeString()` produces a different value on the server (at render time) than on the client (at hydration time, milliseconds later), and `toLocaleTimeString()` can also differ by timezone/locale between the two runtimes. That mismatch is the actual bug.\n\nThe smallest correct fix depends on what the timestamp is for:\n\n**Option A — compute it once on the server and pass it down.** If the displayed time is \"when this page/data was generated\" rather than \"now in the user's browser,\" compute the timestamp in the Server Component and pass it as a prop (or render it from data). Both server render and hydration then use the identical string, and there's no mismatch:\n\n```tsx\n// Server Component\nexport default function Page() {\n const generatedAt = new Date().toISOString();\n return <Clock generatedAt={generatedAt} />;\n}\n```\n(Format it to a fixed, locale-independent string if you can, since `toLocaleTimeString()` formatting itself can vary across runtimes.)\n\n**Option B — if it needs to be \"live\" clock time in the user's local zone,** render a stable placeholder during SSR and update it in an effect after mount. The server and the initial client render both produce the placeholder, so hydration matches; the real time appears only after the component has mounted on the client:\n\n```tsx\n'use client';\nimport { useState, useEffect } from 'react';\n\nexport function Clock() {\n const [time, setTime] = useState<string | null>(null);\n\n useEffect(() => {\n const tick = () => setTime(new Date().toLocaleTimeString());\n tick();\n const id = setInterval(tick, 1000);\n return () => clearInterval(id);\n }, []);\n\n return <span>{time ?? '--:--:--'}</span>;\n}\n```\n\nThe key property in both cases: the value rendered during SSR and during the client's first render is identical, and any nondeterministic value is introduced only after mount.\n\nWhat not to do:\n\n- Don't add `suppressHydrationWarning` — it hides this instance but leaves the nondeterministic render in place, and the same pattern will keep producing mismatches (and can mask real ones).\n- Don't wrap the whole component/route in `<ClientOnly>` or set `ssr: false` just to avoid hydrating it — that turns off server rendering for content that doesn't need it and routes around the root cause.\n- Don't branch on `typeof window` to pick the value during render — that itself causes the server and client to render differently, which is the same bug in a different form.\n\nSo: determine whether the time is a fixed \"generated at\" value (Option A, preferable — pure, no client component needed) or a live clock (Option B, mount-effect update). After applying it, re-run the build/load the page and confirm the warning is gone with no new warnings, and confirm no `suppressHydrationWarning`/`ssr: false` was introduced.",
1759
+ "outputSha256": "a80ef62c0f5aef2b3d69d4e3a172f6141ae9d9eb5df80403f0bd746b1a9a0380",
1760
+ "promptSha256": "2d54a6305ba4c9b2f70e1eea7eab4d4984cf681079cb175f70d3b98fcd4a2819",
1761
+ "deterministic": [
1762
+ true
1763
+ ],
1764
+ "judge": {
1765
+ "verdict": "pass",
1766
+ "reason": "Option A passes a server-computed timestamp as a prop (generatedAt={generatedAt}); Option B shows the mounted-flag/effect pattern with useState(null) + useEffect setting time after mount and a placeholder. Both concrete code changes are shown. suppressHydrationWarning is only mentioned to warn against it, not recommended."
1767
+ },
1768
+ "passed": true
1769
+ }
1770
+ ]
1771
+ },
1772
+ {
1773
+ "id": "narrow-use-client-not-widen",
1774
+ "kind": "behavior",
1775
+ "prompt": "Next.js flags a Server/Client boundary build error because a small icon-button inside my Server Component page uses the useState hook, and that hook needs client-side rendering to run at all. How should I fix it?",
1776
+ "strictness": "high",
1777
+ "trials": 10,
1778
+ "passes": 10,
1779
+ "passRate": 1,
1780
+ "passAtK": 1,
1781
+ "grader": "regex+judge",
1782
+ "status": "ran",
1783
+ "trialRecords": [
1784
+ {
1785
+ "output": "The error is correct and specific: the icon-button file (or wherever `useState` is called) needs the client runtime, but it's currently being treated as part of the Server Component tree. The fix is small and local.\n\n**Root cause:** the module that calls `useState` has no `'use client'` directive, so Next.js compiles it as a Server Component. Server Components can't run hooks.\n\n**Smallest correct fix:** add `'use client'` to the top of the file that actually defines the icon-button — not to the page, not to the layout, not to any ancestor. If the button currently lives inside a file that also exports server-only siblings, extract it into its own file first, then add the directive to just that file:\n\n```tsx\n'use client';\n\nimport { useState } from 'react';\n\nexport function IconButton({ ... }) {\n const [open, setOpen] = useState(false);\n // ...\n}\n```\n\nThe Server Component page can keep importing and rendering `<IconButton />` unchanged — Next.js will treat it as a client island and still server-render everything else around it.\n\n**What not to do:**\n\n- Don't move `'use client'` up to the page or a shared layout. That ships the whole subtree — including content that never needed interactivity — to the client and defeats the point of the Server Component boundary.\n- Don't try to defer `useState` behind a ref or condition to \"avoid\" the boundary. Hooks must be called unconditionally at the top of a Client Component; there's no server-side equivalent, and the error will just resurface.\n- Don't silence it with a build/config change. There's nothing to silence — the boundary is genuinely wrong.\n\n**Verify:** re-run the exact command that failed (`npx next build`, after `npx tsc --noEmit`), confirm it exits clean with no new warnings, and confirm the `'use client'` directive sits on the smallest component that needs the client runtime — the icon-button file, not a shared ancestor.",
1786
+ "outputSha256": "4e7442ea6c7e723d0271f3298dbe60ccd7385115a98bf858391176cd8b06886d",
1787
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1788
+ "deterministic": [
1789
+ true
1790
+ ],
1791
+ "judge": {
1792
+ "verdict": "pass",
1793
+ "reason": "Answer states add 'use client' to the file defining the icon-button, 'not to the page, not to the layout, not to any ancestor', and names extracting it: 'If the button currently lives inside a file that also exports server-only siblings, extract it into its own file first, then add the directive to just that file', with a code example. It also warns against moving the directive to the page/layout."
1794
+ },
1795
+ "passed": true
1796
+ },
1797
+ {
1798
+ "output": "Add `'use client'` at the top of the icon-button's own file, not the page.\n\nConcretely:\n\n1. Locate the icon-button component. If it currently lives inside the same file as the Server Component page, extract it into its own file (e.g. `IconButton.tsx`).\n2. Add `'use client'` as the first line of that extracted file, above the imports.\n3. Leave the page itself as a Server Component — do not add `'use client'` to it.\n\nWhy this is the right fix:\n\n- The boundary error exists because `useState` needs the client runtime, and the nearest `'use client'` directive is above the file that uses it. The directive marks a boundary; putting it on the icon-button makes the button a Client Component while everything around it stays on the server.\n- Adding `'use client'` to the page would push the entire page subtree — including any server-only data fetching, large imports, or Node-only modules — into the client bundle. That fixes the error but breaks the reason the page was a Server Component in the first place.\n\nThings to watch after the change:\n\n- Props passed from the Server Component page into the Client Component must be serializable (plain objects, strings, numbers, arrays, promises in newer versions). If the page currently passes a function, a class instance, or a Node-only object as a prop to the icon-button, you'll hit a new error — move that interaction into the Client Component instead, or convert the value to a serializable form.\n- If the icon-button imports anything that itself needs the server (a `'use server'` action is fine, a direct `fs`/`node:*` import is not), that import must move out of the Client Component.\n- The icon button's click handler and any `useEffect` stay where they are — they now have a valid client context.\n\nVerify with the same command that produced the original failure (`npx next build` or your project's wrapper), confirm it exits 0 with no new warnings about serializable props or missing client boundaries, and confirm the page file still has no `'use client'` at its top.\n\nDo not fix this by setting `ssr: false` on a parent route or by moving `'use client'` up to a layout — those make the error disappear while shipping far more of the page to the client than the button actually needs.",
1799
+ "outputSha256": "56f92cb34356331d742ecf5c4bbd5cd0a0cf12ea9a762f054d756df79d7e72a1",
1800
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1801
+ "deterministic": [
1802
+ true
1803
+ ],
1804
+ "judge": {
1805
+ "verdict": "pass",
1806
+ "reason": "Answer states 'Add `'use client'` at the top of the icon-button's own file, not the page' (criterion 1) and step 1 names extracting it into its own file (e.g. IconButton.tsx) if it lives in the page file (criterion 2). It also warns against adding it to the page/layout, which is not a fail."
1807
+ },
1808
+ "passed": true
1809
+ },
1810
+ {
1811
+ "output": "Extract the icon-button into its own file and put `'use client'` at the top of that file only — not the page.\n\nConcretely:\n\n1. Create `IconButton.tsx` (or `.jsx`) alongside the page, move the button component there, add `'use client'` as the first line.\n2. Import it into the Server Component page as usual — the page itself stays a Server Component.\n\nWhy this and not the alternatives:\n\n- **Don't put `'use client'` on the page.** That would ship the whole page subtree (and everything it imports) to the client, defeating server rendering for content that never needed it. The boundary should sit on the smallest component that actually uses `useState`.\n- **Don't strip the `useState`.** If the button genuinely needs state (e.g. toggle, hover/focus state, an in-flight indicator), the hook is legitimate and the component really is a Client Component.\n- **If the state is trivial and can be expressed without a hook** — e.g. pure CSS `:hover`/`:active`, or a `<details>`/`<summary>` toggle — consider dropping the `useState` entirely and keeping the button as a Server Component. That's the smaller fix when it works, but only if the interactivity is actually expressible that way.\n\nProps you pass across the boundary must be serializable: plain values, strings, numbers, or Server Actions as event handlers. Don't pass functions, class instances, or non-serializable objects from the Server Component page into `IconButton`; if the button needs a callback, make it a Server Action (`'use server'`) or handle the interaction entirely inside the Client Component.\n\nVerify with `npx next build` (and `npx tsc --noEmit` if you use TypeScript) — it should exit 0 with no boundary error, and the page should still be server-rendered in the build output.",
1812
+ "outputSha256": "1575172812e4fb9b6cd1b7b6dd17d5dee207317d0cbabd08fb2bc9907135e893",
1813
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1814
+ "deterministic": [
1815
+ true
1816
+ ],
1817
+ "judge": {
1818
+ "verdict": "pass",
1819
+ "reason": "Answer states to extract the icon-button into its own file and put 'use client' at the top of that file only, not the page (step 1: 'Create IconButton.tsx ... add 'use client' as the first line'; 'not the page'). It explicitly names extracting into its own component file so the directive is scoped to just it, and warns against putting it on the page."
1820
+ },
1821
+ "passed": true
1822
+ },
1823
+ {
1824
+ "output": "Extract that icon-button into its own file with `'use client'` at the top, and import it back into the Server Component page.\n\nConcretely:\n\n1. Create (or move) the icon-button into its own module, e.g. `components/IconButton.tsx`, and add `'use client'` as the very first line of that file. It keeps its `useState` usage as-is.\n\n2. In your Server Component page, import `IconButton` as before. It now renders as a client island inside the otherwise-server-rendered page.\n\nWhat not to do:\n\n- Don't add `'use client'` to the page itself. That would ship the whole page — and everything it renders — to the client, defeating the server rendering that content never needed.\n- Don't move the directive up to a shared layout or ancestor. Same problem, larger blast radius.\n- Don't try to \"hoist\" the button's hook out into the parent to avoid the directive. The hook genuinely needs the client runtime; the boundary has to be drawn somewhere, and the button is the smallest place for it.\n\nWhy this is the smallest correct fix: the error names exactly one thing that needs the client runtime — the `useState` in the icon-button. So the client boundary should be exactly that button, not its parent, not the page, not a layout. The page stays a Server Component and continues to fetch/render on the server; only the interactive button crosses into the client bundle.\n\nOne thing to check while you're in there: if the button file currently also exports server-only siblings (data fetchers, `fs`/`node:` imports, `async` page helpers), don't drag those into the new client module. Extract only the button. If the boundary error persists after the extraction, it usually means something the button imports still needs the client runtime too — trace that import and pull it into the same client file rather than widening `'use client'` upward.\n\nVerify afterward by re-running the command that failed (`npx tsc --noEmit` and `npx next build`) and confirming it exits clean with no new warnings, and that the `'use client'` directive sits on the button component, not on the page.",
1825
+ "outputSha256": "c8b13309f874aae2c23e73134e6b6ca1fc2589a71951991088a2f3c7ab3b490d",
1826
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1827
+ "deterministic": [
1828
+ true
1829
+ ],
1830
+ "judge": {
1831
+ "verdict": "pass",
1832
+ "reason": "Answer states to extract the icon-button into its own file (components/IconButton.tsx) with 'use client' as first line, and explicitly warns not to add 'use client' to the page or shared layout. Both pass criteria concretely satisfied; no fail criterion holds."
1833
+ },
1834
+ "passed": true
1835
+ },
1836
+ {
1837
+ "output": "The root cause here isn't the page — it's the icon-button. A component that calls `useState` (or `useEffect`, or attaches an `onClick`, etc.) can only run in the client runtime, so it must be a Client Component. The fix is to make *only that button* a Client Component, not the whole page.\n\n**Step 1: Split the button into its own file and mark it `'use client'`.**\n\nMove the icon-button (and only the icon-button) out of the Server Component page into its own file, e.g. `components/IconButton.tsx`:\n\n```tsx\n'use client';\n\nimport { useState } from 'react';\n\nexport default function IconButton({ /* props */ }) {\n const [active, setActive] = useState(false);\n // ...onClick, etc.\n return <button onClick={() => setActive(a => !a)}>{/* icon */}</button>;\n}\n```\n\n**Step 2: Import it into the Server Component page as a normal import.**\n\nYour page stays a Server Component with no directive:\n\n```tsx\nimport IconButton from '@/components/IconButton';\n\nexport default function Page() {\n return (\n <main>\n {/* server-rendered content */}\n <IconButton />\n </main>\n );\n}\n```\n\nReact/Next handles the boundary automatically: the page renders on the server, and `IconButton` is hydrated on the client. A Server Component *importing* a Client Component is exactly the intended pattern — the error only fires when the file itself lacks `'use client'` while using client-only APIs.\n\n**Why not the shorter-looking alternatives:**\n\n- Adding `'use client'` to the page (or a shared layout) makes the whole subtree client-rendered, shipping server-renderable content to the browser and forfeiting the benefit of the Server Component. Only do this if the page genuinely cannot be a Server Component.\n- The directive must sit on the *smallest* component that actually needs client behavior — here, the button. Don't push it up to an ancestor.\n\n**Verify:** re-run `npx tsc --noEmit` and `npx next build` — both should exit 0 with no boundary error and no new warnings. Confirm the fix is confined to the button's file plus the page's import line, with no directive added to any shared ancestor.",
1838
+ "outputSha256": "4b80b34a50cdd328398d8033f834a8499ccdc248aae24860ac9dc4cab263f000",
1839
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1840
+ "deterministic": [
1841
+ true
1842
+ ],
1843
+ "judge": {
1844
+ "verdict": "pass",
1845
+ "reason": "Step 1 explicitly says move the icon-button into its own file components/IconButton.tsx and add 'use client' there, and Step 2 keeps the page a Server Component with no directive. It also warns against adding 'use client' to the page/layout, satisfying both pass criteria and no fail criterion."
1846
+ },
1847
+ "passed": true
1848
+ },
1849
+ {
1850
+ "output": "Extract the icon-button into its own file and put `'use client'` at the top of that file, then import it into the Server Component page as a normal child component.\n\nConcretely:\n\n1. Create `IconButton.tsx` (or whatever the component is named) containing just that button, with `'use client'` as the first line:\n ```tsx\n 'use client';\n import { useState } from 'react';\n\n export function IconButton(props) {\n const [open, setOpen] = useState(false);\n // ...\n return <button onClick={() => setOpen(o => !o)}>...</button>;\n }\n ```\n2. In the Server Component page, import `IconButton` and render it as usual. The page itself stays a Server Component — no `'use client'` added to it, and definitely not to any shared layout or ancestor.\n\nWhy this is the right fix:\n\n- The boundary error exists because the page is a Server Component (no `'use client'`), and it (or something it imports) pulls in `useState`. Marking only the leaf client is the smallest change — the rest of the page keeps server rendering.\n- Do **not** add `'use client'` to the page or a parent layout. That would ship the entire subtree to the client and defeat server rendering for content that never needed it.\n\nPractical notes:\n\n- If `IconButton` is currently defined inline in the same file as the page, you must move it to a separate module — a single file can't be both a Server Component and contain a `'use client'` boundary for just one part.\n- If the page passes props into the button, pass only serializable values (strings, numbers, plain objects) across the boundary. You can't pass functions, except the special `'use server'` action functions. If you need a click handler that calls server code, define a Server Action with `'use server'` and pass it as the `onClick` prop.\n- If `useState` is genuinely being used for something that doesn't need interactivity (e.g., a purely presentational toggle), consider whether it should be state at all — but if it's a real interactive toggle, the extracted Client Component is correct.\n\nAfter the change, re-run the exact failing command (`npx next build`) and confirm it exits 0 with no new boundary warnings before reporting it fixed.",
1851
+ "outputSha256": "80f4bb4313d8e5549fddb255369cfa682adc4a3994930bf58caefd1fb000f12b",
1852
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1853
+ "deterministic": [
1854
+ true
1855
+ ],
1856
+ "judge": {
1857
+ "verdict": "pass",
1858
+ "reason": "Answer states to put 'use client' at top of the icon-button's own file (IconButton.tsx) and import it into the Server Component page, explicitly saying the page stays a Server Component with no 'use client' added to it or any shared layout. It also names extracting the inline button into a separate module file, noting a single file can't be both."
1859
+ },
1860
+ "passed": true
1861
+ },
1862
+ {
1863
+ "output": "Add `'use client'` at the top of the smallest component that actually needs the client runtime — the icon-button itself — not the Server Component page that wraps it.\n\nConcretely:\n\n1. Open the icon-button's file. If that button lives inside the same file as the Server Component page, extract it into its own file first (e.g. `IconButton.tsx`), because `'use client'` applies to a whole module and would pull the entire page — and everything it imports — into the client bundle.\n2. Add `'use client'` as the very first statement of the extracted `IconButton.tsx`, above its imports.\n3. Import the new client component into the Server Component page and render it as a child, passing any data it needs as props.\n\nWhat not to do:\n\n- Don't add `'use client'` to the page, the layout, or any shared ancestor. That ships the entire subtree to the client and quietly defeats server rendering for content that never needed it.\n- Don't restructure the button to avoid `useState` (e.g. lifting the state into the Server Component) — a Server Component cannot hold interactive state, and `useState` genuinely requires the client runtime. The boundary belongs at the button.\n- Don't reach for `ssr: false` / `<ClientOnly>`-style suppression or a widened `tsconfig` to get past the error. The error is correct.\n\nThe mental model: the boundary belongs on the smallest component that needs interactivity. Everything above it stays a Server Component and keeps its server-rendered benefits; only the button crosses over.",
1864
+ "outputSha256": "10b29be77464afb51bcf683efcb6fa3e543e6f92f7494b0d1fdd56d1cbd6da15",
1865
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1866
+ "deterministic": [
1867
+ true
1868
+ ],
1869
+ "judge": {
1870
+ "verdict": "pass",
1871
+ "reason": "Criterion 1: answer says add 'use client' to 'the icon-button itself — not the Server Component page that wraps it' and warns against adding it to page/layout. Criterion 2: step 1 explicitly says extract the button into its own file (e.g. IconButton.tsx) if it lives in the page file, so the directive is scoped to just it. No fail criterion holds."
1872
+ },
1873
+ "passed": true
1874
+ },
1875
+ {
1876
+ "output": "The root cause is a boundary violation: the page is a Server Component (no `'use client'`), and it imports a component that calls `useState`. `useState` requires the client runtime, so the build correctly refuses to bundle it into a server-rendered module. The fix is to make the icon-button a Client Component — and only the icon-button.\n\n**The fix**\n\nExtract the icon-button into its own file and mark that file with the directive:\n\n```tsx\n// components/IconButton.tsx\n'use client';\n\nimport { useState } from 'react';\n\nexport function IconButton({ onClick, children }: Props) {\n const [pressed, setPressed] = useState(false);\n return (\n <button\n aria-pressed={pressed}\n onClick={(e) => { setPressed((p) => !p); onClick?.(e); }}\n >\n {children}\n </button>\n );\n}\n```\n\nThen import it into the Server Component page as normal:\n\n```tsx\n// app/page.tsx (stays a Server Component — no 'use client')\nimport { IconButton } from '@/components/IconButton';\n\nexport default async function Page() {\n const data = await getData(); // still server-side\n return (\n <main>\n <h1>{data.title}</h1>\n <IconButton onClick={...}>star</IconButton>\n </main>\n );\n}\n```\n\n**Why extract, and why the directive goes on the smallest component**\n\nThe `'use client'` directive marks a *module boundary*: it says \"this file and everything it imports is part of the client graph.\" If the icon-button's hook lives in a file bundled together with server-only siblings, you'd be forced to mark the whole bundle — dragging unrelated code to the client. Pulling the button into its own file lets the directive sit on exactly the component that needs the client runtime, which is the smallest correct scope.\n\n**What not to do**\n\n- Do **not** put `'use client'` at the top of `app/page.tsx`. That ships the entire page — including all the server-side data fetching and any server-only imports — to the client, defeating the reason the page is a Server Component in the first place.\n- Do **not** move the directive up to a shared layout or ancestor component for the same reason: it cascades client-ness down to every child.\n- Do **not** reach for `dynamic(..., { ssr: false })` or similar to route around the error. That's not a boundary fix; it's disabling server rendering for the button and hiding the real problem.\n\n**Note on the interaction with the Server Component**\n\nThe icon-button can still be passed data as props from the Server Component — props are serialized across the boundary. What it *cannot* receive is a non-serializable value (a function defined on the server, a class instance, a raw DB handle). If your button needs to trigger a server-side effect, the client component should call a `'use server'` action rather than receiving a server closure as a prop. If the `onClick` you're threading in is a plain server-side function, that's a second boundary problem to solve with a Server Action, not by hoisting `'use client'`.\n\n**Verify**\n\nRe-run the exact command that failed (e.g. `npx next build`, and `npx tsc --noEmit` if the project type-checks separately). Confirm it exits 0 with no new warnings about the boundary. Then confirm the directive sits only on `IconButton.tsx` and no shared ancestor or the page itself was marked `'use client'`. The report should name the actual cause — \"the icon-button used `useState`, so it needed to be its own Client Component\" — not just \"the build passes now.\"",
1877
+ "outputSha256": "5bd7c98de944299fd602c081888c09397d97af569221dc36e35742080660b526",
1878
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1879
+ "deterministic": [
1880
+ true
1881
+ ],
1882
+ "judge": {
1883
+ "verdict": "pass",
1884
+ "reason": "Answer states the fix is to make only the icon-button a Client Component, showing 'use client' at top of components/IconButton.tsx (its own file), and explicitly says to extract the icon-button into its own file so the directive sits on exactly that component. It also warns against putting 'use client' on app/page.tsx or a shared layout, satisfying both pass criteria and no fail criterion."
1885
+ },
1886
+ "passed": true
1887
+ },
1888
+ {
1889
+ "output": "The smallest correct fix: pull the icon-button out into its own file and put `'use client'` at the top of that file only.\n\n```tsx\n// components/IconButton.tsx\n'use client';\nimport { useState } from 'react';\n\nexport function IconButton({ label, icon }: { label: string; icon: React.ReactNode }) {\n const [active, setActive] = useState(false);\n return (\n <button aria-label={label} aria-pressed={active} onClick={() => setActive(a => !a)}>\n {icon}\n </button>\n );\n}\n```\n\nThen in your Server Component page, import it and pass only serializable props:\n\n```tsx\n// app/page.tsx (stays a Server Component)\nimport { IconButton } from '@/components/IconButton';\n\nexport default function Page() {\n return (\n <main>\n <h1>Dashboard</h1>\n <IconButton label=\"Favorite\" icon={<span>★</span>} />\n </main>\n );\n}\n```\n\nWhy this is the right shape:\n\n- The directive goes on the leaf component that actually owns the hook, not on `page.tsx`. Marking the page `'use client'` would also ship the `<main>`/`<h1>` markup and every sibling to the browser and give up server rendering for content that never needed it.\n- The page stays a Server Component, so it keeps its server-side data fetching and only the interactive button hydrates on the client.\n- Props crossing the boundary must be serializable. Passing a plain string (`label`) and a rendered element (`icon`) is fine; passing a function, a class instance, or a `Date` directly is not. If the button needs a handler from the page, pass a server action reference, not an inline closure defined in the Server Component.\n\nAfter this, re-run the exact command that failed (`npx next build`, plus `npx tsc --noEmit` if your scripts separate them) and confirm it exits 0 with no new warnings.\n\nDo not fix this by adding `'use client'` to the page or a shared layout, and do not disable type checking or the build check to make the error go away — both hide the boundary violation instead of resolving it.",
1890
+ "outputSha256": "e46d33f8a1e00536afba9d883e356e66e1d3e3eda9e422594a4ed83048c7cb9f",
1891
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1892
+ "deterministic": [
1893
+ true
1894
+ ],
1895
+ "judge": {
1896
+ "verdict": "pass",
1897
+ "reason": "Answer states 'pull the icon-button out into its own file and put 'use client' at the top of that file only' (criterion 1 & 2), shows components/IconButton.tsx with 'use client' and page.tsx staying a Server Component, and explicitly warns against adding 'use client' to the page or shared layout."
1898
+ },
1899
+ "passed": true
1900
+ },
1901
+ {
1902
+ "output": "Add `'use client'` to the smallest component that actually needs it — the icon-button itself — not the page.\n\nConcretely:\n\n1. **Locate the hook.** `useState` (or `useEffect`, an event handler, etc.) can only run in a Client Component. Right now that icon-button is being treated as a Server Component because it lives in a file with no `'use client'` directive.\n\n2. **Extract if needed.** If the icon-button is defined inline inside the page file (or bundled with server-only siblings), move it into its own file — e.g. `components/IconButton.tsx` — and put `'use client'` as the very first line of that file. If it's already its own file, just add the directive at the top.\n\n```tsx\n'use client'; // smallest component that needs the client runtime\nimport { useState } from 'react';\n\nexport function IconButton({ ... }) {\n const [open, setOpen] = useState(false);\n // ...\n}\n```\n\n3. **Leave the page as a Server Component.** The page keeps its existing work (data fetching, server-only imports, etc.) and simply imports `<IconButton />`. Only the button subtree ships to the client.\n\n4. **Pass data across the boundary as props.** If the button needs data the page fetched on the server, hand it down as a serializable prop — don't import the server-only module into the client file.\n\nWhy not the other options:\n\n- **Don't put `'use client'` on the page/layout.** That would force the whole page (and everything it renders) into the client bundle, throwing away server rendering for content that never needed it.\n- **Don't reimplement the interactivity without state** just to avoid the directive — the hook is genuinely needed here, so the boundary is the right fix.\n- **Don't suppress or widen anything** (no `suppressHydrationWarning`, no tsconfig changes) — those don't address a boundary error at all.\n\nAfter the change, re-run the command that produced the failure (`npx tsc --noEmit` / `npx next build`) and confirm it exits 0. The root cause to report: the icon-button file lacked `'use client'`, so it was compiled as a Server Component despite needing the client runtime for `useState`.",
1903
+ "outputSha256": "8f8b40973b8a2c99586ffce7e3c02600ac5128b2df393ad25580048ad370a4eb",
1904
+ "promptSha256": "b8602bf5cea2e846e1e94e190394e284c1a490ee710195ac92b69d334a638be9",
1905
+ "deterministic": [
1906
+ true
1907
+ ],
1908
+ "judge": {
1909
+ "verdict": "pass",
1910
+ "reason": "Answer states add 'use client' to the icon-button itself, not the page ('Add `'use client'` to the smallest component that actually needs it — the icon-button itself — not the page'). It names extraction: 'If the icon-button is defined inline inside the page file... move it into its own file — e.g. components/IconButton.tsx — and put 'use client' as the very first line.' Both pass criteria met; no fail criterion."
1911
+ },
1912
+ "passed": true
1913
+ }
1914
+ ]
1915
+ }
1916
+ ],
1917
+ "verdict": "fail",
1918
+ "scope": "bundled",
1919
+ "skillDigest": "5d0a0c2a542fd6e0935e31de058554033bc669efd16602ab90da5cfb76a91cbc",
1920
+ "catalogDigest": "139cc7e7c63f9a3fcb3560551b7740c5642c828db2570e2f40728089218cc6d4",
1921
+ "judgePromptVersion": "2026-09-25.1",
1922
+ "runner": "deepseek",
1923
+ "model": "deepseek-chat",
1924
+ "runnerPromptVersion": "2026-09-25.1",
1925
+ "recordedAt": "2026-09-25T15:03:35.099Z",
1926
+ "judge": "deepseek",
1927
+ "judgeModel": "deepseek-chat"
1928
+ },
1929
+ {
1930
+ "schemaVersion": "1.0.0",
1931
+ "skillId": "nextjs-nuxt/nextjs-nuxt-upgrade-migration",
1932
+ "strictness": "high",
1933
+ "trials": 10,
1934
+ "triggerAccuracy": {
1935
+ "truePositive": 7,
1936
+ "falsePositive": 3,
1937
+ "positives": 7,
1938
+ "negatives": 6
1939
+ },
1940
+ "evidence": "authored",
1941
+ "scenarios": [
1942
+ {
1943
+ "id": "trigger-positive-1",
1944
+ "kind": "trigger-positive",
1945
+ "prompt": "This Next.js app still uses the pages router everywhere, I need to migrate it over to the app router, where should I begin?",
1946
+ "strictness": "high",
1947
+ "trials": 1,
1948
+ "passes": 1,
1949
+ "passRate": 1,
1950
+ "passAtK": 1,
1951
+ "grader": "trigger-rank-fork-family",
1952
+ "status": "ran",
1953
+ "deterministic": true
1954
+ },
1955
+ {
1956
+ "id": "trigger-positive-2",
1957
+ "kind": "trigger-positive",
1958
+ "prompt": "Upgrade this Next.js project's pages/api routes to app router route handlers",
1959
+ "strictness": "high",
1960
+ "trials": 1,
1961
+ "passes": 1,
1962
+ "passRate": 1,
1963
+ "passAtK": 1,
1964
+ "grader": "trigger-rank-fork-family",
1965
+ "status": "ran",
1966
+ "deterministic": true
1967
+ },
1968
+ {
1969
+ "id": "trigger-positive-3",
1970
+ "kind": "trigger-positive",
1971
+ "prompt": "This project is still running on the old Nuxt 2 major version and we need to bring it forward to Nuxt 3, what does that upgrade actually involve?",
1972
+ "strictness": "high",
1973
+ "trials": 1,
1974
+ "passes": 1,
1975
+ "passRate": 1,
1976
+ "passAtK": 1,
1977
+ "grader": "trigger-rank-fork-family",
1978
+ "status": "ran",
1979
+ "deterministic": true
1980
+ },
1981
+ {
1982
+ "id": "trigger-positive-4",
1983
+ "kind": "trigger-positive",
1984
+ "prompt": "This page currently uses getServerSideProps for its data fetching, how do I convert that to the app router equivalent once we migrate it?",
1985
+ "strictness": "high",
1986
+ "trials": 1,
1987
+ "passes": 1,
1988
+ "passRate": 1,
1989
+ "passAtK": 1,
1990
+ "grader": "trigger-rank-fork-family",
1991
+ "status": "ran",
1992
+ "deterministic": true
1993
+ },
1994
+ {
1995
+ "id": "trigger-positive-5",
1996
+ "kind": "trigger-positive",
1997
+ "prompt": "As part of bringing this page up to Nuxt 3, its old asyncData data-fetching call needs to become the new composable-based equivalent, how do I do that conversion?",
1998
+ "strictness": "high",
1999
+ "trials": 1,
2000
+ "passes": 1,
2001
+ "passRate": 1,
2002
+ "passAtK": 1,
2003
+ "grader": "trigger-rank-fork-family",
2004
+ "status": "ran",
2005
+ "deterministic": true
2006
+ },
2007
+ {
2008
+ "id": "trigger-positive-6",
2009
+ "kind": "trigger-positive",
2010
+ "prompt": "As part of our Nuxt 3 upgrade, I need to migrate this store from Vuex over to Pinia, what does that conversion involve?",
2011
+ "strictness": "high",
2012
+ "trials": 1,
2013
+ "passes": 1,
2014
+ "passRate": 1,
2015
+ "passAtK": 1,
2016
+ "grader": "trigger-rank-fork-family",
2017
+ "status": "ran",
2018
+ "deterministic": true
2019
+ },
2020
+ {
2021
+ "id": "trigger-positive-7",
2022
+ "kind": "trigger-positive",
2023
+ "prompt": "I still have a component built with the old Options API, and I want it rewritten to use script setup as part of moving to Nuxt 3, how should I approach that?",
2024
+ "strictness": "high",
2025
+ "trials": 1,
2026
+ "passes": 1,
2027
+ "passRate": 1,
2028
+ "passAtK": 1,
2029
+ "grader": "trigger-rank-fork-family",
2030
+ "status": "ran",
2031
+ "deterministic": true
2032
+ },
2033
+ {
2034
+ "id": "trigger-negative-1",
2035
+ "kind": "trigger-negative",
2036
+ "prompt": "Add a brand new page to this already-on-app-router Next.js project",
2037
+ "strictness": "high",
2038
+ "trials": 1,
2039
+ "passes": 0,
2040
+ "passRate": 0,
2041
+ "passAtK": 0,
2042
+ "grader": "trigger-rank-fork-family",
2043
+ "status": "ran",
2044
+ "deterministic": true
2045
+ },
2046
+ {
2047
+ "id": "trigger-negative-2",
2048
+ "kind": "trigger-negative",
2049
+ "prompt": "Review this Next.js server action for auth issues",
2050
+ "strictness": "high",
2051
+ "trials": 1,
2052
+ "passes": 0,
2053
+ "passRate": 0,
2054
+ "passAtK": 0,
2055
+ "grader": "trigger-rank-fork-family",
2056
+ "status": "ran",
2057
+ "deterministic": true
2058
+ },
2059
+ {
2060
+ "id": "trigger-negative-3",
2061
+ "kind": "trigger-negative",
2062
+ "prompt": "Write a test for this Nuxt composable",
2063
+ "strictness": "high",
2064
+ "trials": 1,
2065
+ "passes": 1,
2066
+ "passRate": 1,
2067
+ "passAtK": 1,
2068
+ "grader": "trigger-rank-fork-family",
2069
+ "status": "ran",
2070
+ "deterministic": true
2071
+ },
2072
+ {
2073
+ "id": "trigger-negative-4",
2074
+ "kind": "trigger-negative",
2075
+ "prompt": "Fix this build error blocking next build right now",
2076
+ "strictness": "high",
2077
+ "trials": 1,
2078
+ "passes": 1,
2079
+ "passRate": 1,
2080
+ "passAtK": 1,
2081
+ "grader": "trigger-rank-fork-family",
2082
+ "status": "ran",
2083
+ "deterministic": true
2084
+ },
2085
+ {
2086
+ "id": "trigger-negative-5",
2087
+ "kind": "trigger-negative",
2088
+ "prompt": "Upgrade this plain React 18 app to React 19 with no routing framework involved",
2089
+ "strictness": "high",
2090
+ "trials": 1,
2091
+ "passes": 0,
2092
+ "passRate": 0,
2093
+ "passAtK": 0,
2094
+ "grader": "trigger-rank-fork-family",
2095
+ "status": "ran",
2096
+ "deterministic": true
2097
+ },
2098
+ {
2099
+ "id": "trigger-negative-6",
2100
+ "kind": "trigger-negative",
2101
+ "prompt": "Migrate this Python 2 script to Python 3 syntax",
2102
+ "strictness": "high",
2103
+ "trials": 1,
2104
+ "passes": 1,
2105
+ "passRate": 1,
2106
+ "passAtK": 1,
2107
+ "grader": "trigger-rank-fork-family",
2108
+ "status": "ran",
2109
+ "deterministic": true
2110
+ },
2111
+ {
2112
+ "id": "getserversideprops-to-app-router",
2113
+ "kind": "behavior",
2114
+ "prompt": "I have a Next.js Pages Router page using getServerSideProps to fetch an order by ID before render. How do I migrate this to the App Router?",
2115
+ "strictness": "high",
2116
+ "trials": 10,
2117
+ "passes": 10,
2118
+ "passRate": 1,
2119
+ "passAtK": 1,
2120
+ "grader": "regex+judge",
2121
+ "status": "ran",
2122
+ "trialRecords": [
2123
+ {
2124
+ "output": "In the App Router that per-request fetch becomes a direct `await` inside the now-Server Component — `getServerSideProps` doesn't exist as an exported function anymore.\n\n**Before (Pages Router):**\n```tsx\n// pages/orders/[id].tsx\nexport async function getServerSideProps({ params }) {\n const order = await getOrder(params.id);\n return { props: { order } };\n}\n\nexport default function OrderPage({ order }) {\n return <div>{order.id}</div>;\n}\n```\n\n**After (App Router):**\n```tsx\n// app/orders/[id]/page.tsx\nexport default async function OrderPage({ params }: { params: { id: string } }) {\n const order = await getOrder(params.id);\n return <div>{order.id}</div>;\n}\n```\n\nKey points:\n\n- **Delete `getServerSideProps` entirely.** There's no per-component equivalent — you just fetch at the top of the async Server Component. The component being `async` and running on the server is the whole mechanism.\n- **`params` is passed as a prop** to the page component (in Next 15+ it's a `Promise` you await); there's no destructured `{ params }` argument to the fetch function the way `getServerSideProps` had.\n- **Rendering mode is now the route default, not an exported function.** Server Components are dynamic by default (equivalent to per-request SSR), so you get `getServerSideProps` behavior for free. If instead you wanted ISR you'd add `export const revalidate = <seconds>`; if you wanted it fully static, `export const dynamic = 'force-static'`. Don't add any of these unless you actually want to change away from the default — the point is the `getServerSideProps`-equivalent is the default, not an export.\n- **No `props` return object.** Return the JSX directly from the page component; the data is just a local variable.\n\nWatch out for one thing carried over from the source: if any child component rendered by this page uses `useState`/`useEffect`/browser APIs, it needs `'use client'` once it lives under `app/` — the Pages Router client-rendered everything implicitly, the App Router does not. The page itself fetching data and rendering plain markup can stay a Server Component with no directive.\n\nVerify by running the app's build/typecheck and loading `/orders/<id>` — confirm it still renders per-request (not baked static) by checking the data reflects a fresh `getOrder` call, since a migration here can silently flip the route's caching behavior if a `revalidate`/`dynamic` export got copied over from somewhere.",
2125
+ "outputSha256": "6d142d0ad862c0fa29052527ed1bdd82e95169f4be817cc166c8980666432cc1",
2126
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2127
+ "deterministic": [
2128
+ true
2129
+ ],
2130
+ "judge": {
2131
+ "verdict": "pass",
2132
+ "reason": "Answer shows fetch moved into async Server Component body in app/orders/[id]/page.tsx (\"const order = await getOrder(params.id)\"), reads params from the component's own params argument, and notes interactive children using useState/useEffect need 'use client'. No getServerSideProps workaround recommended."
2133
+ },
2134
+ "passed": true
2135
+ },
2136
+ {
2137
+ "output": "Since I have no tools or file access here, this is a plain-text migration walkthrough for your specific case (a Pages Router page using `getServerSideProps` for a per-request order fetch, moving to the App Router).\n\n## 1. What the old code looks like\n\n`pages/orders/[id].tsx`:\n\n```tsx\nexport async function getServerSideProps(ctx) {\n const order = await getOrder(ctx.params.id)\n if (!order) return { notFound: true }\n return { props: { order } }\n}\n\nexport default function OrderPage({ order }) {\n return <div>{order.id}</div>\n}\n```\n\n## 2. The App Router shape\n\nMove the file to `app/orders/[id]/page.tsx`. The Server Component itself is now the data-fetching function — there is no separate `getServerSideProps` export.\n\n```tsx\n// app/orders/[id]/page.tsx\nexport default async function OrderPage({ params }) {\n const { id } = await params // params is a Promise in Next 15+\n const order = await getOrder(id)\n if (!order) notFound() // import { notFound } from 'next/navigation'\n return <div>{order.id}</div>\n}\n```\n\nThe mapping, piece by piece:\n\n- `getServerSideProps` body → inlined `await` at the top of the (now async) default-exported Server Component. The component *is* the server function.\n- `ctx.params.id` → the `params` prop. Note the version detail: in Next.js 15+ `params` is a `Promise` you `await`; in Next 13/14 it is a plain synchronous object. Check your `package.json` for the installed major before writing this line.\n- `return { notFound: true }` → call `notFound()` from `next/navigation` directly. There is no props-return contract anymore.\n- `return { props: { order } }` → just return JSX using the local `order` value; nothing is serialized across a props boundary.\n- `return { redirect: ... }` (if you have that branch) → `redirect()` from `next/navigation`.\n\n## 3. Why this keeps the original rendering mode\n\n`getServerSideProps` means \"render this page on every request\" (SSR, dynamic). A `page.tsx` with no static hint is **dynamic by default** in the App Router, so plain inlining preserves the per-request behavior — exactly what you want for a live order lookup. You do **not** need `export const dynamic = 'force-dynamic'` in the normal case; only reach for it if a caching layer or a parent segment config accidentally makes the route static.\n\nThe one thing to verify explicitly (per the skill's Step 4): confirm the route is actually dynamic in the built output, not accidentally prerendered. A \"still builds\" green check does not prove the freshness mode is unchanged. Set `export const revalidate = 0` or `export const dynamic = 'force-dynamic'` only if you observe it being cached.\n\n## 4. The trap for this specific page\n\nAn order-detail page usually renders interactive bits — a quantity stepper, a \"cancel order\" button with `onClick`, a client-side status poller. Once that code lives in `app/`, it runs on the server by default. Any child component that uses `useState`, `useEffect`, event handlers, or browser APIs must get an explicit `'use client'` at the top of its own file.\n\nDo **not** put `'use client'` on `page.tsx` itself — that would force the whole route to render on the client and defeat the server-side order fetch you just migrated. Keep `page.tsx` server, fetch the order there, and pass plain data down to small `'use client'` children:\n\n```tsx\n// app/orders/[id]/page.tsx (stays a Server Component)\nimport CancelButton from './CancelButton'\n...\nreturn (\n <>\n <div>{order.id}</div>\n <CancelButton orderId={order.id} /> {/* its own file has 'use client' */}\n </>\n)\n```\n\nThis is the main correctness risk in this migration — the Pages Router rendered everything on the client implicitly, so a missing `'use client'` was never a problem before. Rely on an explicit check, not just the compiler, since indirect browser-API use can slip past the build the way a direct call would be caught.\n\n## 5. Related pieces on the same page\n\n- If the page currently sets `<Head><title>Order {order.id}</title></Head>`, replace it with a `generateMetadata` export that awaits `params` the same way and fetches the title data (or reuse the order). `next/head` does not work in `app/`.\n- If it uses a per-page `_app.tsx` layout wrapper, that becomes a `layout.tsx` in the route segment.\n- If the order data was also fetched through `pages/api/orders/[id].ts`, migrate that separately to `app/api/orders/[id]/route.ts` exporting a named `GET` function — but the Server Component above should call `getOrder()` directly and skip the internal HTTP hop entirely.\n\n## 6. Migrating this one route — not the whole app\n\nDo this route at a time. `pages/` and `app/` coexist, so `app/orders/[id]/page.tsx` can land and be verified while every other route stays on the Pages Router. Run build/typecheck after this single route moves, confirm the dynamic-freshness behavior, confirm any interactive children are explicitly client, and only then move the next route. Report it as \"orders/[id] migrated; remaining routes still on pages/\" — not \"migration complete.\"",
2138
+ "outputSha256": "ad0deb375f6ac678357e6a805b5650c8c6c9d84b553c123e2b8372871b590e5d",
2139
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2140
+ "deterministic": [
2141
+ true
2142
+ ],
2143
+ "judge": {
2144
+ "verdict": "pass",
2145
+ "reason": "Criterion 1: §2 shows the fetch inlined in the async default-exported Server Component app/orders/[id]/page.tsx, stating 'there is no separate getServerSideProps export'. Criterion 2: maps 'ctx.params.id → the params prop' with `const { id } = await params`. Criterion 3: §4 requires interactive children (useState, onClick) to get their own 'use client' file, keeping page.tsx server. No fail criterion present."
2146
+ },
2147
+ "passed": true
2148
+ },
2149
+ {
2150
+ "output": "Here's how to migrate a Pages Router `getServerSideProps` page to the App Router for an \"order by ID\" page.\n\n**Before (Pages Router): `pages/orders/[id].tsx`**\n\n```tsx\nimport { GetServerSideProps } from 'next';\n\nexport const getServerSideProps: GetServerSideProps = async (ctx) => {\n const res = await fetch(`https://api.example.com/orders/${ctx.params!.id}`);\n const order = await res.json();\n return { props: { order } };\n};\n\nexport default function OrderPage({ order }: { order: Order }) {\n return <div>{order.id} — {order.status}</div>;\n}\n```\n\n**After (App Router): `app/orders/[id]/page.tsx`**\n\n```tsx\ntype Props = { params: Promise<{ id: string }> };\n\nexport default async function OrderPage({ params }: Props) {\n const { id } = await params;\n const res = await fetch(`https://api.example.com/orders/${id}`);\n const order: Order = await res.json();\n\n return <div>{order.id} — {order.status}</div>;\n}\n```\n\nKey mapping and notes:\n\n1. **`getServerSideProps` disappears entirely.** Its whole job was \"fetch on every request before render.\" In the App Router that's just the default behavior of an `async` Server Component — you `await` the fetch directly in the component body. There is no equivalent exported function; you delete it.\n\n2. **Dynamic params are now a `params` prop, and in current Next.js it's a Promise.** You `await params` before reading `id` (older App Router versions passed a plain object; check your Next.js version — this is exactly the kind of thing that differs by version). The `ctx.params.id` becomes `params.id`.\n\n3. **This is dynamic by default — which matches `getServerSideProps`.** Good, the rendering mode is preserved: no `revalidate` export, no `dynamic = 'force-static'`, so the route is rendered per request. This is the \"dynamic\" default and is the correct match for `getServerSideProps` semantics. Do not add caching config unless you *intend* to change freshness — a common migration bug is accidentally making this static.\n\n4. **No `'use client'` needed on this file** as long as the page only renders the fetched data and doesn't use `useState`/`useEffect`/onClick/etc. It's a Server Component, which is the point. If you nest a child component that needs interactivity, add `'use client'` to *that* child, not to the page.\n\n5. **The search-params case.** If the original used `ctx.query` / `ctx.params`, note that in the App Router `params` are the route segments (`[id]`) and `searchParams` (also a Promise in current versions) are the query string — they're split, whereas Pages Router lumped query access together.\n\n6. **Error/not-found handling.** `getServerSideProps` could `return { notFound: true }`. The App Router equivalent is calling `notFound()` from `next/navigation` inside the component (which throws a `NOT_FOUND` and renders the nearest `not-found.tsx`). If your original page did that for a missing order, import `notFound` and call it after the fetch when the order doesn't exist.\n\n7. **Type imports.** `GetServerSideProps` comes from `next` and no longer applies; remove it. The props type is now just the component's own signature with `params` (and optionally `searchParams`).\n\n**Migration order suggestion:** since Pages Router and App Router can coexist, you can create `app/orders/[id]/page.tsx` and leave the old `pages/orders/[id].tsx` in place, verify the new route builds and behaves identically (same data freshness), then delete the old page. That keeps the migration incremental rather than an all-at-once rewrite.\n\nOne thing to confirm in `package.json`: the exact Next.js version, because `params` being a Promise (rather than a plain object) landed at a specific major release. If you're on an older App Router version, drop the `await` on `params`.",
2151
+ "outputSha256": "9e5139e394e8b67ed0e589e87d48f9d8ba1dc37e2ca3623b057609a27a9a903e",
2152
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2153
+ "deterministic": [
2154
+ true
2155
+ ],
2156
+ "judge": {
2157
+ "verdict": "pass",
2158
+ "reason": "Point 1: 'getServerSideProps disappears entirely... you await the fetch directly in the component body' with async OrderPage in app/orders/[id]/page.tsx. Point 2: 'ctx.params.id becomes params.id', read from the component's params prop. Point 3: point 4 says add 'use client' to the interactive child, not the page. No fail criterion: it deletes getServerSideProps rather than calling it."
2159
+ },
2160
+ "passed": true
2161
+ },
2162
+ {
2163
+ "output": "Short answer: the `getServerSideProps` function goes away entirely, and the `fetch` moves directly into the `page.tsx` component body, because in the App Router that page is a Server Component by default and is already per-request/dynamic.\n\nConcretely, the mapping is:\n\n**Pages Router (before)**\n```tsx\n// pages/orders/[id].tsx\nexport async function getServerSideProps(context) {\n const res = await fetch(`https://api.example.com/orders/${context.params.id}`);\n const order = await res.json();\n return { props: { order } };\n}\nexport default function OrderPage({ order }) {\n return <div>{order.id}</div>;\n}\n```\n\n**App Router (after)**\n```tsx\n// app/orders/[id]/page.tsx\nexport default async function OrderPage({ params }) {\n const res = await fetch(`https://api.example.com/orders/${params.id}`);\n const order = await res.json();\n return <div>{order.id}</div>;\n}\n```\n\nKey points for this specific migration:\n\n1. **No replacement export.** There is no `getServerSideProps` equivalent to write — you delete it and inline the fetch. The route is dynamic by default in the App Router, which is exactly the `getServerSideProps` semantics (fresh data per request).\n\n2. **`params` replaces `context.params`.** In recent Next.js versions `params` is a Promise, so you'd `await params` before destructuring `id`. Check your target version's docs, since this changed across majors.\n\n3. **Add `export const dynamic = 'force-dynamic'` only if needed.** Plain `fetch` in a Server Component with no caching opt-in gives dynamic rendering by default, so the equivalent of `getServerSideProps` behavior is already the default. You typically don't need to add anything to preserve the getServerSideProps semantics — but verify, don't assume.\n\n4. **Watch for a silent flip to static.** If you (or a wrapper) pass `cache: 'force-cache'` or set `revalidate` on the fetch, you can accidentally turn a previously-always-fresh page into a cached/static one. Since the original was `getServerSideProps` (always fresh), make sure you are not introducing caching it didn't have.\n\n5. **`'use client'` boundary.** Keep the page itself a Server Component. If a child component inside it uses `useState`, `useEffect`, or browser APIs, that child needs its own `'use client'` — the Pages Router rendered it client-side implicitly, the App Router does not. Don't add `'use client'` to the `page.tsx` itself, or you lose the direct-fetch benefit.\n\n6. **Loading/error states.** If the old page had a client-side fetch fallback or a `_app`-level data layer, those don't carry over automatically; handle them with `loading.tsx` / `error.tsx` in the route segment if needed.\n\nVerify by building/typechecking that route and confirming the order page still returns fresh data per request (not a stale cached copy) before marking it migrated.",
2164
+ "outputSha256": "7c54fcb08ba2c1fe1eafc4fbd4b180d86bc915f19f7d27f064f0b3c8c2292429",
2165
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2166
+ "deterministic": [
2167
+ true
2168
+ ],
2169
+ "judge": {
2170
+ "verdict": "pass",
2171
+ "reason": "Answer shows fetch inlined into async page.tsx body (app/orders/[id]/page.tsx) with no getServerSideProps export (point 1), reads params.id replacing context.params (point 2), and point 5 states interactive children using useState/useEffect need their own 'use client' since App Router doesn't render client-side implicitly (point 3). No workaround keeping getServerSideProps."
2172
+ },
2173
+ "passed": true
2174
+ },
2175
+ {
2176
+ "output": "Short answer: the page becomes an async Server Component that `await`s the fetch directly — `getServerSideProps` itself disappears, because in the App Router \"fetch inside a Server Component\" *is* the per-request data-loading path that `getServerSideProps` used to be. Since your data is per-order and request-scoped, the route stays dynamic by default, so you don't set any caching/revalidate option.\n\nRough shape before (Pages Router):\n\n```tsx\n// pages/orders/[id].tsx\nexport async function getServerSideProps({ params }) {\n const order = await getOrder(params.id);\n return { props: { order } };\n}\nexport default function OrderPage({ order }) {\n return <div>{order.id}</div>;\n}\n```\n\nAfter (App Router):\n\n```tsx\n// app/orders/[id]/page.tsx\nexport default async function OrderPage({ params }) {\n const { id } = await params; // params is a Promise in recent Next.js\n const order = await getOrder(id);\n return <div>{order.id}</div>;\n}\n```\n\nKey points for this specific migration:\n\n1. **`params` handling.** In current Next.js versions `params` is a Promise in Server Components, so `await params` (or the equivalent for your version — check `package.json` before assuming, since this changed across 15.x). Older App Router versions give you a plain object.\n\n2. **No `getServerSideProps`, no export.** There's no `getStaticProps`/`getServerSideProps`/`getStaticPaths` equivalent export to write. The fetch just happens in the component body. Because you're not opting into static caching, the route renders on each request, which matches the old GSSP semantics. (If you *did* want ISR instead, that would be `export const revalidate = <seconds>` — but you said per-order, request-scoped, so leave it off.)\n\n3. **The `'use client'` boundary.** Keep `page.tsx` as a Server Component so the fetch stays on the server. If any child component (a quantity stepper, a cancel button, a cart toggle) uses `useState`/`useEffect`/onClick handlers/browser APIs, move *that* into its own file with `'use client'` at the top and pass the fetched data down as props. Don't put `'use client'` at the top of `page.tsx` — that would turn the whole thing client-side and you'd lose the server fetch.\n\n4. **Error/not-found cases.** A failed order lookup that used to return `{ notFound: true }` from GSSP becomes `notFound()` called from the Server Component (imported from `next/navigation`). A redirect that used to be `{ redirect: {...} }` becomes `redirect(...)`.\n\n5. **Metadata.** If the old page used `next/head` to set a title like \"Order #123\", replace it with `generateMetadata` in the same route, which can re-run the fetch (or share a cached `fetch`/deduped helper) to get the order.\n\n6. **Route handlers / links.** Nothing else needs to change for reading. If other pages link to this route via `next/link`, those still work; the route file location moved from `pages/orders/[id].tsx` to `app/orders/[id]/page.tsx` and the URL is unchanged.\n\n7. **Verify the rendering mode didn't flip.** After migrating, confirm you didn't accidentally make it static. A component that only does a plain `fetch` with no `cache`/`revalidate` options, or a non-fetch async call, stays dynamic per request, which is what you want. Don't add `export const dynamic = 'force-static'` and don't rely on Next's default fetch caching to silently serve a stale order — for per-order data, treat it as always fresh.\n\nMigrate this route on its own, run the build/typecheck, and confirm the order refetches on reload before moving to the next `pages/` route. The `pages/` and `app/` directories can coexist while you do this route by route.",
2177
+ "outputSha256": "05d516ab5289f30b40dc816a007e8acf1cf215b347b692aa471f190b60f12b4f",
2178
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2179
+ "deterministic": [
2180
+ true
2181
+ ],
2182
+ "judge": {
2183
+ "verdict": "pass",
2184
+ "reason": "Answer shows fetch moved into async Server Component body in app/orders/[id]/page.tsx (\"export default async function OrderPage({ params })\" awaiting getOrder), reads id from the component's own params argument (\"const { id } = await params\"), and point 3 requires extracting useState/onClick UI into its own 'use client' file with data passed as props. No getServerSideProps workaround recommended."
2185
+ },
2186
+ "passed": true
2187
+ },
2188
+ {
2189
+ "output": "Migrate that page to an App Router Server Component and replace `getServerSideProps` with a direct `await` fetch inside the component. `getServerSideProps` has no App Router equivalent export — its per-request behavior is simply the App Router's default for a dynamic route, so you delete the function and inline the data fetch.\n\n**Structural changes**\n\n- Move `pages/orders/[id].tsx` → `app/orders/[id]/page.tsx`.\n- The page must be an `async` function component; its return value is JSX, not `{ props }`.\n- `params` is no longer a prop. In a Server Component page, read the route param via the `params` prop of the page: `export default async function Page({ params }: { params: { id: string } })`.\n\n**Data fetching**\n\n- Delete `getServerSideProps` entirely. Do the fetch directly:\n\n```tsx\nexport default async function Page({ params }: { params: { id: string } }) {\n const order = await getOrder(params.id);\n if (!order) notFound(); // from next/navigation, replaces the { notFound: true } return\n return <OrderView order={order} />;\n}\n```\n\n- `getServerSideProps`'s `{ notFound: true }` → call `notFound()` from `next/navigation`.\n- `{ redirect: ... }` → call `redirect()` from `next/navigation`.\n- Cache/freshness: `getServerSideProps` was always per-request. The App Router's default for this dynamic route is also dynamic, so you need nothing extra to preserve it — but be explicit. If you ever want to *change* it to cached/ISR, that becomes `export const revalidate = <seconds>` or `export const dynamic = 'force-static'`. Do not add these unless the original really was static; adding `revalidate` here would silently flip a per-request page to cached.\n\n**Client boundary**\n\n- `getServerSideProps` data was serialized into a client-rendered page. If `OrderView` uses `useState`/`useEffect`/browser APIs, it now needs an explicit `'use client'` at the top of that file, or the page will fail to render it server-side. The page itself stays a Server Component; pass the fetched `order` down as a prop to the client component. Do not put `'use client'` on the page just to keep the old feel — that would defeat fetching on the server.\n- Plain presentational children can stay server components; only the interactive boundary needs the directive.\n\n**Head / metadata**\n\n- If the old page set the title via `next/head`, replace `<Head>` with a `metadata` export or, when the title depends on the fetched order, `export async function generateMetadata({ params })` — note this refetches or should reuse a cached fetch.\n\n**API routes (if relevant)**\n\n- If this order was fetched through `pages/api/orders/[id].ts`, that route can become `app/api/orders/[id]/route.ts` exporting a named `GET` function, but for a page fetch you should generally call the data layer directly rather than round-tripping through your own API route.\n\n**Suggested increments**\n\n1. Create `app/orders/[id]/page.tsx` with the fetch and the order view, leave the `pages/` version in place so both coexist.\n2. Add `'use client'` only to the component(s) that actually need interactivity, passing `order` as a prop.\n3. Build/typecheck, confirm the page still renders per-request (no accidental `revalidate`), and only then delete `pages/orders/[id].tsx`.\n\nVerify: build passes, the interactive child has its `'use client'` boundary, the page's rendering mode is still dynamic (not silently cached), and `notFound()`/`redirect()` replace the old return-value forms. Report which routes remain on the Pages Router rather than calling the whole app migrated.",
2190
+ "outputSha256": "1f75625d66a9592bf80f95a53c6fbf46735eb07a85d1085da3c004035763e19a",
2191
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2192
+ "deterministic": [
2193
+ true
2194
+ ],
2195
+ "judge": {
2196
+ "verdict": "pass",
2197
+ "reason": "Criterion 1: 'replace getServerSideProps with a direct await fetch inside the component' and code shows async Page fetching getOrder(params.id). Criterion 2: 'read the route param via the params prop' with { params }: { params: { id: string } }. Criterion 3: OrderView using useState needs explicit 'use client', page stays Server Component, pass order as prop. No fail criterion present."
2198
+ },
2199
+ "passed": true
2200
+ },
2201
+ {
2202
+ "output": "Migrate that page to the App Router like this:\n\n**1. Move the file and drop the `getServerSideProps` export.**\n`pages/orders/[id].tsx` becomes `app/orders/[id]/page.tsx`. The `getServerSideProps` function goes away entirely — you fetch directly inside the component, which is now a Server Component by default.\n\n**2. Fetch inline, using the new `params` prop.**\n`getServerSideProps` received `context.params`; in the App Router, dynamic segment values arrive as the `params` prop (async in current Next.js versions):\n\n```tsx\n// app/orders/[id]/page.tsx\nexport default async function OrderPage({ params }: { params: Promise<{ id: string }> }) {\n const { id } = await params;\n const order = await getOrderById(id);\n return <OrderDetail order={order} />;\n}\n```\n\n**3. The per-request behavior is the default — no export needed.**\nThis is the key mapping: `getServerSideProps` meant \"render on every request.\" In the App Router that is simply the default behavior of a Server Component that fetches at render time, so you don't declare anything. (The *opposite* — caching/ISR — is what requires an explicit `export const revalidate = <seconds>`, or `export const dynamic = 'force-dynamic'` if you need to be explicit about opting out of caching.)\n\n**4. Handle the not-found case with the App Router idiom, not a props shape.**\n`getServerSideProps` returned `{ notFound: true }`. Replace that with `notFound()` from `next/navigation`:\n\n```tsx\nimport { notFound } from 'next/navigation';\n\nconst order = await getOrderById(id);\nif (!order) notFound(); // renders the nearest not-found.tsx\n```\n\nLikewise, `getServerSideProps`'s `redirect` becomes `redirect()` from `next/navigation`.\n\n**5. If the detail component is interactive, add the boundary.**\nIf `<OrderDetail>` (or anything below) uses `useState`, `useEffect`, or browser APIs, it needs `'use client'` at the top of its own file. Under the Pages Router that interactivity was implicit; the App Router defaults to server rendering, so the boundary must be explicit. A good split is: fetch in the server `page.tsx`, pass the data down to a client component that handles interactivity.\n\n**Things this migration does *not* involve:** `getStaticPaths` (that's the `getStaticProps` sibling, which becomes `generateStaticParams`) and `next/head` (only relevant if this page set a `<title>` — that becomes a `metadata` export or `generateMetadata`).\n\n**Verify before calling it done:** the page still renders per-request (not accidentally cached), the not-found path renders correctly, and — if anything component-side is interactive — the `'use client'` boundary is present and the build/typecheck passes. If the Interactivity is buried a few components deep, add the boundary explicitly rather than relying on the build error to surface it.",
2203
+ "outputSha256": "df63dd8186de4781f0f337eedf2227a15c1937e5350437f6b290670ad508506e",
2204
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2205
+ "deterministic": [
2206
+ true
2207
+ ],
2208
+ "judge": {
2209
+ "verdict": "pass",
2210
+ "reason": "Criterion 1: answer says getServerSideProps 'goes away entirely — you fetch directly inside the component' in app/orders/[id]/page.tsx, shown as async OrderPage. Criterion 2: shows params prop replacing context.params ('dynamic segment values arrive as the params prop', const { id } = await params). Criterion 3: step 5 says interactive parts using useState/useEffect need 'use client' in their own file since App Router defaults to server rendering. No fail criterion: it never keeps getServerSideProps."
2211
+ },
2212
+ "passed": true
2213
+ },
2214
+ {
2215
+ "output": "Migrate that page to a Server Component under `app/`, and replace the `getServerSideProps` function with a direct fetch inside the component. Because Server Components render per-request by default, the per-request behavior `getServerSideProps` gave you is preserved without any extra config — you don't need to declare anything to keep it dynamic.\n\nThe shape of the change:\n\n**Before (Pages Router):**\n```tsx\n// pages/orders/[id].tsx\nexport async function getServerSideProps({ params }) {\n const res = await fetch(`https://api.example.com/orders/${params.id}`)\n const order = await res.json()\n return { props: { order } }\n}\n\nexport default function OrderPage({ order }) {\n return <div>{order.id}</div>\n}\n```\n\n**After (App Router):**\n```tsx\n// app/orders/[id]/page.tsx\nasync function getOrder(id: string) {\n const res = await fetch(`https://api.example.com/orders/${id}`)\n return res.json()\n}\n\nexport default async function OrderPage({ params }) {\n const order = await getOrder(params.id)\n return <div>{order.id}</div>\n}\n```\n\nKey points for this specific case:\n\n- **`params` is now a prop**, not part of a context argument. In current Next.js versions `params` is async, so you may need `const { id } = await params` — check your project's Next.js version, since this differs between majors.\n- **Only the fetch moves into the component.** If the order data is only rendered as text/HTML, the component stays a Server Component and you're done. If any child needs `useState`/`useEffect`/browser APIs, mark *that* component `'use client'` — don't mark the page itself client-side just because it fetches.\n- **Keep it dynamic.** You don't add anything to preserve `getServerSideProps`' per-request semantics. (Contrast: `getStaticProps` + `revalidate` would become `export const revalidate = <seconds>` for ISR; `getStaticPaths` would become `generateStaticParams`.) Since you want per-request freshness, leave the route dynamic and make sure nothing in the fetch opts it into caching — if your fetch options or an upstream `cache` setting make the order response static, the page will silently serve stale or wrong orders. Verify this explicitly rather than assuming from a successful build.\n- **Route location changes**: `pages/orders/[id].tsx` becomes `app/orders/[id]/page.tsx`. During the transition both trees can coexist, so migrate this route on its own and confirm it before moving the next one.",
2216
+ "outputSha256": "739cf546110ad059fd203c00ef750d94ff45ad675d5342f4943ac36613b8a049",
2217
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2218
+ "deterministic": [
2219
+ true
2220
+ ],
2221
+ "judge": {
2222
+ "verdict": "pass",
2223
+ "reason": "Answer shows fetch moved into async page component body (app/orders/[id]/page.tsx, `const order = await getOrder(params.id)`), reads route param from the component's own `params` prop (not context.params), and notes interactive UI needing useState/useEffect must be extracted into its own 'use client' component. No getServerSideProps workaround recommended."
2224
+ },
2225
+ "passed": true
2226
+ },
2227
+ {
2228
+ "output": "Short answer: delete the `getServerSideProps` export, make the page an async Server Component that fetches directly with `await`, and keep the dynamic param via `params`. Also add `dynamic = 'force-dynamic'` (or rely on the dynamic `params` behavior) so you don't accidentally turn a per-request page into a static one.\n\nHere's the mechanical mapping.\n\n**Before (Pages Router), `pages/orders/[id].tsx`:**\n\n```tsx\nimport { GetServerSideProps } from 'next'\n\nexport const getServerSideProps: GetServerSideProps = async (ctx) => {\n const order = await getOrder(ctx.params!.id as string)\n if (!order) return { notFound: true }\n return { props: { order } }\n}\n\nexport default function OrderPage({ order }: { order: Order }) {\n return <div>{order.total}</div>\n}\n```\n\n**After (App Router), `app/orders/[id]/page.tsx`:**\n\n```tsx\nimport { notFound } from 'next/navigation'\n\nasync function getOrder(id: string) {\n const res = await fetch(`https://api.example.com/orders/${id}`, {\n cache: 'no-store', // this is the gSSR equivalent: per-request, not cached\n })\n if (res.status === 404) return null\n return res.json() as Promise<Order>\n}\n\nexport default async function OrderPage({\n params,\n}: {\n params: { id: string }\n}) {\n const order = await getOrder(params.id)\n if (!order) notFound()\n return <div>{order.total}</div>\n}\n```\n\nKey points:\n\n- **The fetch happens in the component itself.** There's no separate exported data function anymore — `getServerSideProps` collapses into the async Server Component body. `getServerSideProps`'s \"runs on every request\" semantics map to `fetch(..., { cache: 'no-store' })` (or, if you're not using `fetch`, the route being dynamic).\n- **`notFound: true`** becomes a call to `notFound()` imported from `next/navigation`.\n- **Dynamic params** come through the `params` prop instead of `ctx.params`, and the route segment directory (`app/orders/[id]/`) replaces the file-based `[id].tsx` convention.\n- **Rendering mode**: since this page depends on `params.id` with no `generateStaticParams`, it renders dynamically per request by default. If you want to be explicit — or your page reads headers/cookies and you want to be safe — add `export const dynamic = 'force-dynamic'` near the top of the file. This is the piece most likely to silently flip: if you later add `generateStaticParams`, the page becomes static unless the fetch is marked `no-store`.\n\nTwo things to watch:\n\n- **No `'use client'` here.** This page has no state/effects/browser APIs, so it stays a Server Component — do *not* add `'use client'` to the page itself. If a nested child component needs interactivity, that child gets `'use client'`, not the page.\n- **If a call site in the JSX uses hooks or browser APIs**, extract that interactive part into a separate client component and pass `order` down as props rather than marking the whole page client — marking the page `'use client'` would break the direct server-side fetch.\n\nIf the page is fetched from an internal API route you also plan to migrate, note that the App Router Route Handler under `app/api/.../route.ts` exports named `GET`/`POST` functions — not the single default-exported `handler` from `pages/api` — so migrate that alongside if the page depends on it.\n\nMigrate and verify this one route first (build/typecheck + confirm it's actually rendering per-request), then move to the next page rather than batching the whole `pages/` tree.",
2229
+ "outputSha256": "5816bc1ed705b667aa299a36668b37929f393662179a1a51b253970149875dac",
2230
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2231
+ "deterministic": [
2232
+ true
2233
+ ],
2234
+ "judge": {
2235
+ "verdict": "pass",
2236
+ "reason": "Answer shows fetch inside async Server Component body in app/orders/[id]/page.tsx ('getServerSideProps collapses into the async Server Component body'), reads params.id from the component's params prop instead of ctx.params, and notes interactive parts need their own 'use client' child component ('extract that interactive part into a separate client component'). No getServerSideProps workaround recommended."
2237
+ },
2238
+ "passed": true
2239
+ },
2240
+ {
2241
+ "output": "Short answer: drop `getServerSideProps` entirely, make the page an `async` Server Component that fetches directly, and read the route param from the `params` prop instead of the `query` context object. Because the default in `app/` is dynamic (per-request), you get the same \"fresh on every request\" behavior `getServerSideProps` gave you without any extra config — that's the key insight, the per-request-vs-static decision is now a route-level default rather than a separate exported function.\n\n**Before (Pages Router):**\n\n```tsx\n// pages/orders/[id].tsx\nimport type { GetServerSideProps } from 'next';\n\nexport const getServerSideProps: GetServerSideProps = async (ctx) => {\n const res = await fetch(`https://api.example.com/orders/${ctx.params!.id}`, {\n headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },\n });\n if (!res.ok) return { notFound: true };\n const order = await res.json();\n return { props: { order } };\n};\n\nexport default function OrderPage({ order }: { order: Order }) {\n return <div><h1>Order {order.id}</h1><p>{order.status}</p></div>;\n}\n```\n\n**After (App Router):**\n\n```tsx\n// app/orders/[id]/page.tsx\nimport { notFound } from 'next/navigation';\n\nexport default async function OrderPage({ params }: { params: { id: string } }) {\n const res = await fetch(`https://api.example.com/orders/${params.id}`, {\n headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },\n cache: 'no-store', // explicit per-request; omit only if you actually want caching\n });\n if (!res.ok) notFound();\n\n const order = await res.json();\n\n return <div><h1>Order {order.id}</h1><p>{order.status}</p></div>;\n}\n```\n\nThe specific mappings applied here:\n\n- **`getServerSideProps` → direct `await fetch` in the component.** No exported data-fetching function at all; the Server Component *is* the server render. This is the core \"don't keep the old API alive inside new code\" move.\n- **`ctx.params.id` → the `params` prop.** Route segment `[id]` is unchanged in directory naming, but the value arrives as a prop, not via a context argument. (In newer Next.js versions `params` is a Promise you `await` — check your version against `package.json` before assuming, since the exact shape differs by major version.)\n- **`return { notFound: true }` → `notFound()`.** Same 404 semantics, imperative call instead of a return object.\n- **The `Authorization` header / `process.env` read stays server-side automatically** — this is a Server Component by default, so secrets never leak, same guarantee `getServerSideProps` gave.\n\nTwo things to watch:\n\n1. **Rendering mode.** The App Router default is dynamic, matching `getServerSideProps`. But if this route is wrapped by an ancestor that sets `export const dynamic = 'force-static'` or a broad `revalidate`, a page that needed per-request freshness could silently go stale. Confirm the route's actual mode explicitly rather than assuming \"it still builds\" means it still behaves the same.\n\n2. **Client interactivity.** If `OrderPage` (or a child it renders) uses `useState`, `useEffect`, event handlers, or browser APIs, that component needs an explicit `'use client'` at the top. The Pages Router client-rendered everything implicitly; the App Router server-renders by default, and a missing boundary is exactly the class of breakage that doesn't always surface as a clean build error. Move the interactive piece into its own client component and pass the fetched `order` down as a prop rather than sprinkling `'use client'` on the fetching page itself.\n\nIf you have sibling pages still on `getServerSideProps`, migrate and verify route by route — `pages/` and `app/` coexist, so you can land this one, run the build/typecheck, and confirm the rendering mode matches the original before moving on.",
2242
+ "outputSha256": "346cadc0835d8ace73fea02a8a2e17338a27f5785cb39b355bcec58f38409220",
2243
+ "promptSha256": "4fee5705b46bd1e809a533d21e34529160040c09f4fab086b3024ad5fc4a1ba1",
2244
+ "deterministic": [
2245
+ true
2246
+ ],
2247
+ "judge": {
2248
+ "verdict": "pass",
2249
+ "reason": "Answer shows async Server Component in app/orders/[id]/page.tsx with direct await fetch, no exported data function (criterion 1). Reads params.id prop instead of ctx.params (criterion 2). Notes interactive UI with useState/useEffect/handlers needs its own 'use client' component, passing order as prop (criterion 3). No getServerSideProps workaround recommended."
2250
+ },
2251
+ "passed": true
2252
+ }
2253
+ ]
2254
+ },
2255
+ {
2256
+ "id": "pinia-not-vuex-wrapper",
2257
+ "kind": "behavior",
2258
+ "prompt": "I'm migrating a Nuxt 2 project with a Vuex store module to Nuxt 3. Should I just wrap the existing Vuex module inside Pinia's defineStore to save time?",
2259
+ "strictness": "high",
2260
+ "trials": 10,
2261
+ "passes": 10,
2262
+ "passRate": 1,
2263
+ "passAtK": 1,
2264
+ "grader": "regex+judge",
2265
+ "status": "ran",
2266
+ "trialRecords": [
2267
+ {
2268
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That produces a store that satisfies neither Vuex's nor Pinia's actual API contract. It's the exact anti-pattern this skill flags as a red flag, because the shape of the two APIs is genuinely different.\n\nA Vuex module is an object with `state`, `mutations`, `actions`, `getters`, and `modules`. Pinia has no mutations at all — mutations and actions collapse into one concept. A `defineStore` setup is either a setup function returning refs/computed/functions, or an options-like object with `state`, `getters`, and `actions`, but the actions are plain functions that directly mutate `state` instead of dispatching commits. Wrapping the old module keeps you calling `commit`/`dispatch` against something that doesn't really implement them, and you end up maintaining a shim no one can reason about.\n\nWhat to do instead:\n\n- Rewrite the module to Pinia's real API. `state` becomes the store's state, `getters` become getters (or `computed` in setup syntax), and each Vuex `mutation` plus its triggering `action` collapses into a single Pinia action that mutates state directly. Drop the `commit` layer entirely.\n- Use `defineStore` with the setup syntax (`ref`/`reactive`/`computed`/functions) or the options syntax — pick one and be consistent across the migration.\n- Migrate module by module, not all at once. Nuxt 3 has no built-in Vuex integration, so if you need a transition period you can keep Vuex installed as a deliberate bridge while you move stores over, but treat that as a temporary step, not the end state.\n- Verify each migrated store against its actual consumers: build/typecheck after each module and confirm the call sites were updated from `commit`/`dispatch` to direct action/property access. A \"still compiles\" check isn't enough if old call sites were left pointing at a shim.\n\nThe time you think you're saving by wrapping is paid back with interest the moment anything needs to read or write that store, because the wrapper doesn't match either framework's mental model. Rewriting to Pinia's actual shape is the migration — do it per module and verify as you go.",
2269
+ "outputSha256": "1a1e95cef6dfc323c2a307a5716a5b5a645b00937e8962def748f5413f594592",
2270
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2271
+ "deterministic": [
2272
+ true
2273
+ ],
2274
+ "judge": {
2275
+ "verdict": "pass",
2276
+ "reason": "Answer says 'No — don't wrap the Vuex module inside defineStore' and calls it an anti-pattern satisfying neither API (crit 1). It concretely describes Pinia's shape: state, getters, actions as plain functions mutating state directly, mutations+actions collapsing, dropping commit layer, setup or options syntax (crit 2). No endorsement of wrapping as a shortcut."
2277
+ },
2278
+ "passed": true
2279
+ },
2280
+ {
2281
+ "output": "No — wrapping the old Vuex module object inside `defineStore` is a red flag the skill calls out explicitly, and it costs you more later than the shortcut saves now.\n\nWhy it's wrong: a Vuex module is shaped around `state` / `mutations` / `actions` with mutations as the only legal way to touch state. Pinia's store shape is `state` / `getters` / `actions`, where state is mutated directly inside actions and there's no mutation layer at all. Wrapping the Vuex object unchanged gives you a store that satisfies neither contract — Vuex code expects `commit('mutation')` to exist, Pinia components expect to call actions and read state, and every consumer ends up confused about which one it's talking to. It also usually drags the `store/index.js` root registration along, which Nuxt 3 doesn't have a built-in Vuex integration for in the first place.\n\nWhat to do instead, module by module:\n\n- Rewrite the store as a `defineStore` that owns its own `state` (a function returning the initial value), `getters` (the old `getters`/computed values), and `actions` (the old actions with their `commit` calls inlined into direct state mutation).\n- Collapse mutations into the actions that called them. A mutation that only did `state.x = payload` becomes a line in the action. A mutation shared across several actions becomes a plain helper or a second action, not a retained mutation layer.\n- Components change from `this.$store.commit(...)` / `mapState` to `const store = useXStore()` called inside `<script setup>`, reading state directly and calling actions directly.\n- If the store is large, migrate it incrementally — one Pinia store per old Vuex module — and allow both to coexist only as a deliberate bridge while you're mid-migration, not as the end state. Finish the pass; don't leave the shim in place.\n\nPer the skill's verification bar, don't call this module migrated until the Nuxt 3 build/typecheck passes for it, every component touching it has been moved off `$store`/`mapState` onto the Composition API store, and the store's read/write behavior matches the original's actual semantics — not just \"it compiles.\" And report it as \"module X migrated, modules Y and Z remain\" rather than a blanket \"Vuex is now Pinia.\"",
2282
+ "outputSha256": "2456c337afb2c34212506639de15220376a0102c3823a1ec1b9846a68d40ba8f",
2283
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2284
+ "deterministic": [
2285
+ true
2286
+ ],
2287
+ "judge": {
2288
+ "verdict": "pass",
2289
+ "reason": "Answer opens with 'No — wrapping the old Vuex module object inside defineStore is a red flag' (criterion 1). It then concretely describes the Pinia shape: 'state (a function returning the initial value), getters ... and actions (the old actions with their commit calls inlined into direct state mutation)', plus collapsing mutations into actions (criterion 2). No endorsement of the shortcut; it explicitly rejects it."
2290
+ },
2291
+ "passed": true
2292
+ },
2293
+ {
2294
+ "output": "No — don't wrap the existing Vuex module inside `defineStore`. That's one of the explicit anti-patterns this migration skill calls out, and it produces a store that satisfies neither API contract. Let me walk through why and what to do instead.\n\n## Why the wrap-a-module approach fails\n\nVuex modules and Pinia stores have structurally different shapes:\n\n- Vuex: `state` is a data object (or a function in some setups), `mutations` are synchronous functions that receive `state` and mutate it, `actions` commit mutations, `getters` receive `state` (and other getters), and everything is wired through a central `store` instance with `dispatch`/`commit`.\n- Pinia: `state` is a function returning an object, `getters` are like computed properties, and `actions` are plain functions that mutate `state` directly — there are no mutations at all. The store is a self-contained `defineStore` result with `this`/direct property access, not a `commit`/`dispatch` indirection layer.\n\nIf you dump `{ state, getters, mutations, actions }` into a `defineStore` setup, the `mutations` are dead code (nothing calls them in Pinia's model), `getters` and `actions` won't receive their expected arguments, and `commit()`/`dispatch()` calls scattered through your components will break. You end up with a shim that compiles but misbehaves — the exact \"satisfies neither API\" failure in the skill's red flags table.\n\n## What to do instead\n\nMigrate the module to Pinia's real API, module by module. A reasonable mapping:\n\n- **Vuex `state`** → Pinia `state` as a function returning the same initial object.\n- **Vuex `getters`** → Pinia `getters` (arrow functions that read `state` or other getters via the store argument).\n- **Vuex `mutations`** → fold the mutation logic directly into the actions that called them (Pinia has no mutations), or into the `state` mutation sites.\n- **Vuex `actions`** → Pinia `actions`; replace `commit('mutation', payload)` with a direct `this.someState = ...` write, and `dispatch('other', ...)` with calling the other action/store directly.\n\nAlso update every call site in components: `this.$store.dispatch(...)` / `this.$store.commit(...)` / `mapState` / `mapGetters` become direct store usage (`const store = useMyStore()`), which pairs naturally with the Options-API-to-`<script setup>` rewrite you're already doing.\n\n## Coexistence during the transition\n\nPinia is the target, but Nuxt 3 has no built-in Vuex integration, so a large store can't always land in one pass. Migrate one module at a time, and if you genuinely need a bridge step where Vuex and Pinia coexist briefly, treat that as a deliberate, temporary transition state — not the end goal. The skill is explicit that a wrapper is fine *only* as a short-lived bridge, never as the shipped result.\n\n## Verify before calling it done\n\n- Build and typecheck after each module migrates, not only at the end.\n- Search the codebase for leftover `commit(`, `dispatch(`, `mapState`, `mapGetters`, and `$store` references — any that remain are unmigrated call sites, not \"done.\"\n- Report exactly which modules moved and which still need a pass, rather than a blanket \"Vuex migrated.\"\n\nThe time you'd save by wrapping is illusory: you'd still have to touch every call site to make it work, and you'd be left maintaining a store that follows neither framework's model. Rewriting to Pinia's actual shape is less total work and produces code that works.",
2295
+ "outputSha256": "6f57735628d62e575be9080366e1b0e1356ab847f898108e8bccdc8e5d8d2f33",
2296
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2297
+ "deterministic": [
2298
+ true
2299
+ ],
2300
+ "judge": {
2301
+ "verdict": "pass",
2302
+ "reason": "Answer opens with 'No — don't wrap the existing Vuex module inside defineStore' (criterion 1), then gives the concrete Pinia rewrite: state as function, getters, actions as own methods, mutations folded into actions, commit/dispatch replaced by direct writes (criterion 2). No endorsement of wrapping as a shortcut; the brief bridge mention is explicitly temporary, not the shipped result."
2303
+ },
2304
+ "passed": true
2305
+ },
2306
+ {
2307
+ "output": "Short answer: no — don't wrap the Vuex module inside `defineStore`. That's explicitly called out as a red flag in this skill, and it's worth being concrete about why, because the shortcut is tempting for exactly the reason you'd expect (a big store module is a lot of surface to rewrite).\n\n## Why the wrapper doesn't actually save time\n\nA Vuex module has a specific shape: `state` as a plain object (or a function returning one), `mutations` as functions that are committed by name, `actions` as functions that receive a `context` object and call `context.commit(...)` / `context.dispatch(...)`, and `getters` that receive `(state, getters)`. Pinia's `defineStore` expects something structurally different: a `state` function returning the reactive state, `getters` that receive the store instance as their first argument, and `actions` that are plain methods mutating `this` directly — no commits, no dispatch, no mutation layer at all.\n\nIf you shove the Vuex object in unchanged, you get a store that isn't really either API. Calls that used to go through `commit` have nothing meaningful to hit, getters that take `(state, getters)` don't line up with how Pinia passes `state`, and any component still calling `this.$store.commit('someModule/doThing')` is now talking to a store that only superficially resembles the old one. You end up debugging a hybrid that satisfies neither contract, which is usually slower than doing the rewrite properly.\n\n## What to do instead\n\n- **Rewrite the module to Pinia's actual shape.** `state` becomes a function, `getters` become functions of the store, `mutations` collapse into the actions (Pinia has no mutation layer — actions just mutate `state` on `this`), and the Vuex `context`-based action bodies get rewritten to use `this` directly.\n- **Migrate module by module, not all at once.** Nuxt 3 has no built-in Vuex integration, so a truly mixed state during the transition needs to be a deliberate, temporary bridge — not the end state. Land each module as a working increment and verify it before moving to the next.\n- **Verify each increment** rather than assuming \"it still builds\" means it's correct. A store that builds cleanly can still be wired to the wrong reactive source or the wrong action names.\n\n## The general principle behind this\n\nThis is the same reason the skill tells you not to migrate the whole `pages/` directory to `app/` in one commit: a migration's value comes from each increment being independently testable and reversible. A shim that keeps the old API \"sort of working\" defeats that — you can't cleanly verify it, you can't cleanly roll it back, and you still have to do the real rewrite later anyway. Better to pay the rewrite cost once, per module, with the target API as the actual destination.",
2308
+ "outputSha256": "3c29a5c89bdabcd34c7de1932b3c0e3ec406a98f2e344803836dcb658f52ab9e",
2309
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2310
+ "deterministic": [
2311
+ true
2312
+ ],
2313
+ "judge": {
2314
+ "verdict": "pass",
2315
+ "reason": "Answer says no to wrapping the Vuex module in defineStore ('don't wrap the Vuex module inside defineStore... isn't really either API'). It concretely describes Pinia's shape: state as a function, getters receiving store instance, actions as plain methods mutating this, mutations collapsing into actions, context rewritten to this. No endorsement of the shortcut."
2316
+ },
2317
+ "passed": true
2318
+ },
2319
+ {
2320
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That's the exact anti-pattern this migration is meant to eliminate, and it's called out as a red flag. A Vuex module and a Pinia store have different API contracts (`state` as a function returning an object vs. Vuex's `state` object, `mutations` vs. direct state writes, `actions` with `commit`/`dispatch` vs. plain functions that mutate state directly, `getters` receiving `state` differently). A shim that wraps the old module object inside `defineStore` satisfies neither API cleanly — you get Vuex-shaped code that Pinia doesn't fully understand and Pinia-shaped code that doesn't match what your components expect, so you've spent migration effort to arrive at something that works worse than either original.\n\nInstead, rewrite the module to Pinia's actual shape:\n\n- `state` becomes the store's `state` (or a setup-style `ref`/`reactive`).\n- Vuex `mutations` disappear — Pinia doesn't have them; components or actions mutate state directly.\n- Vuex `actions` (which used `commit`/`dispatch`) become plain functions that read and write state directly, no `commit` wrapper.\n- Vuex `getters` become Pinia `getters` (or `computed` in setup-style stores).\n- Cross-module calls change from `dispatch('otherModule/action')` to importing and calling the other Pinia store's action directly.\n\nDo this module by module, not in one pass. Nuxt 3 has no built-in Vuex integration, so the clean end state is all-Pinia; if you genuinely need a bridge period while other modules are still on Vuex, keep both systems coexisting as a deliberate, time-boxed step rather than a long-term state — and never the wrapper-inside-`defineStore` trick as that bridge, since it's the one form that's actively broken.\n\nOnce a module is migrated, run the Nuxt 3 build/typecheck and confirm the store's consumers still behave correctly before calling that module done, and report which modules moved vs. remain rather than declaring the whole store migration complete after a partial pass.",
2321
+ "outputSha256": "3e52434c3e8fde522fe68c380a18362b7912134cd57e47afa2fd942dcd6c529d",
2322
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2323
+ "deterministic": [
2324
+ true
2325
+ ],
2326
+ "judge": {
2327
+ "verdict": "pass",
2328
+ "reason": "Answer says 'No — don't wrap the Vuex module inside defineStore' and calls it an anti-pattern that satisfies neither API (criterion 1). It then concretely rewrites to Pinia shape: state function/object, mutations disappear, actions become plain functions without commit/dispatch, getters become Pinia getters/computed (criterion 2). No endorsement of the shortcut."
2329
+ },
2330
+ "passed": true
2331
+ },
2332
+ {
2333
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That produces something that satisfies neither Vuex's nor Pinia's actual API contract, and it's one of the explicit red flags for this migration.\n\nWhat to do instead:\n\nPinia stores are shaped differently from Vuex modules. In Vuex you have `state`, `mutations` (synchronous, committed), `actions` (which commit mutations), and `getters`. In Pinia you have:\n\n- `state` as a function returning the initial state\n- `getters` as plain functions (like computed)\n- `actions` as functions that mutate state directly — no `commit`, no separate mutation layer\n\nSo the migration is a rewrite, not a wrapper:\n\n```js\n// Vuex (Nuxt 2)\nexport default {\n namespaced: true,\n state: () => ({ items: [], loading: false }),\n getters: { count: (s) => s.items.length },\n mutations: {\n SET_ITEMS(s, v) { s.items = v },\n SET_LOADING(s, v) { s.loading = v },\n },\n actions: {\n async fetchItems({ commit }) {\n commit('SET_LOADING', true)\n commit('SET_ITEMS', await api.getItems())\n commit('SET_LOADING', false)\n },\n },\n}\n```\n\n```ts\n// Pinia (Nuxt 3)\nexport const useItemsStore = defineStore('items', {\n state: () => ({ items: [], loading: false }),\n getters: { count: (s) => s.items.length },\n actions: {\n async fetchItems() {\n this.loading = true\n this.items = await api.getItems()\n this.loading = false\n },\n },\n})\n```\n\nNote the collapse: mutations merge into actions, and you assign to `this.x` directly.\n\nMigration mechanics worth following:\n\n- Do it module by module, verifying each one, rather than a full-store rewrite in one commit.\n- Nuxt 3 has no built-in Vuex integration. If you genuinely need a bridge phase, keeping Vuex around as a deliberate, temporary coexistence step is fine — but it's a transition aid, not an end state. Don't let it become permanent.\n- Add `@pinia/nuxt` to `nuxt.config` and re-register any other modules too, since Nuxt 2 → 3 changed the module/plugin config syntax substantially. Check each module's current docs rather than assuming the old config keys still apply.\n- At each call site, `useStore()` / `mapState` / `mapActions` need to move to `useItemsStore()` inside `<script setup>`, and components still on Options API need to be rewritten to `ref`/`reactive`/`computed` rather than just wrapped.\n\nThe \"save time\" instinct is understandable for a large store, but the wrapper gives you a store that will break in confusing ways (mutations that never fire, reactivity that doesn't track, devtools that can't introspect it). The rewrite is more upfront work per module but converges to something that actually works and can be verified incrementally.",
2334
+ "outputSha256": "09fec99dc5256f7d16eb132f008e6a77a594cb16e25739867ff1e879a40be9e9",
2335
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2336
+ "deterministic": [
2337
+ true
2338
+ ],
2339
+ "judge": {
2340
+ "verdict": "pass",
2341
+ "reason": "Answer opens with \"No — don't wrap the Vuex module inside defineStore\" and says it satisfies neither API contract (crit 1). It then describes Pinia's shape: state as function, getters as plain functions, actions mutating state directly with no commit/mutation layer, and shows a concrete Vuex→Pinia rewrite collapsing mutations into actions (crit 2). No endorsement of wrapping as a shortcut."
2342
+ },
2343
+ "passed": true
2344
+ },
2345
+ {
2346
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That's the exact anti-pattern the migration rules call out, and it produces a store that satisfies neither Vuex's nor Pinia's actual contract: it will look migrated (it imports from Pinia) while behaving like neither, and it will be much harder to untangle later than doing the real rewrite now.\n\nHere's what the migration should actually look like:\n\n**Rewrite, don't shim.** A Vuex module like:\n\n```js\nexport default {\n namespaced: true,\n state: () => ({ items: [] }),\n getters: { count: (s) => s.items.length },\n mutations: { ADD(state, item) { state.items.push(item) } },\n actions: { add({ commit }, item) { commit('ADD', item) } }\n}\n```\n\nbecomes a Pinia store — `state` becomes the store's own reactive state, `getters` becomes `computed`-style getter functions, and `mutations` collapse into `actions` (Pinia has no separate mutation layer):\n\n```ts\nexport const useItemsStore = defineStore('items', () => {\n const items = ref([])\n const count = computed(() => items.value.length)\n function add(item) { items.value.push(item) }\n return { items, count, add }\n})\n```\n\nThe mutation/action split merging into just actions is a real semantic change you have to make deliberately, not something a wrapper can paper over.\n\n**Migrate module by module.** Nuxt 3 has no built-in Vuex integration, so a large store should move one module at a time. If you genuinely need a temporary bridge while some modules are still Vuex, that's a deliberate, short-lived coexistence step — not a long-term end state, and not the wrapper you're proposing.\n\n**Related Nuxt 2 → Nuxt 3 work in the same pass:**\n\n- Options API components move to `<script setup>` with `ref`/`reactive`/`computed` — rewrite the behavior, don't just re-export the old object.\n- `asyncData`/`fetch` page hooks become `useAsyncData`/`useFetch` called inside `<script setup>`.\n- Check each Nuxt module's own current docs for its Nuxt 3 config/registration shape — the `nuxt.config.js` module and plugin syntax changed substantially, so don't assume the old config key still works.\n- Middleware moves to `defineNuxtRouteMiddleware` in `middleware/`, referenced per page via `definePageMeta({ middleware: [...] })` unless it's global.\n\n**Verify:** run the build/typecheck after each module migrates, not only at the end, and confirm each migrated route's rendering mode (`ssr`/`routeRules`) still matches what the original page actually needed. Report exactly what moved and what remains — don't call the migration complete for a partial pass.\n\nThe time \"saved\" by wrapping is illusory: you'd still have to rewrite it properly later, on top of a codebase that now has a misleading half-migration in it.",
2347
+ "outputSha256": "d5a25309374200030a93cb6e252cf628a4f021cce1347a12452a35e7218c7131",
2348
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2349
+ "deterministic": [
2350
+ true
2351
+ ],
2352
+ "judge": {
2353
+ "verdict": "pass",
2354
+ "reason": "Answer opens with 'No — don't wrap the Vuex module inside defineStore' and calls it an anti-pattern (crit 1). It shows the concrete rewrite: Vuex module with state/getters/mutations/actions becomes defineStore with ref state, computed getter, and actions, noting mutations collapse into actions (crit 2). No endorsement of wrapping as a shortcut; it explicitly rejects it."
2355
+ },
2356
+ "passed": true
2357
+ },
2358
+ {
2359
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That produces a store that satisfies neither Vuex's nor Pinia's real API contract, and you'll inherit a shim you have to unwrap later anyway. Rewrite it to Pinia's actual shape: a `defineStore` whose `state` is a function returning the old state object, `getters` for what were Vuex getters, and `actions` for the mutations/actions (Pinia collapses mutations and actions into one concept — synchronous state changes go straight into actions). Don't keep Vuex's mutation-type constants or `commit`/`dispatch` calls; those have no Pinia equivalent.\n\nA few things worth doing as you go:\n\n- Migrate module by module, not the whole store in one pass. A large Vuex store can coexist with Pinia only as a deliberate, temporary bridge during the transition — not as a long-term end state. Pick one module, convert it, verify, then move to the next.\n- Check each Nuxt 2 module's own current docs for its Nuxt 3 registration syntax before assuming the old `nuxt.config` key still works — module and plugin registration changed substantially between Nuxt 2 and 3.\n- While you're in each component that consumed that store, migrate it from Options API (`export default { data() {...}, methods: {...} }`) to `<script setup>` with `ref`/`reactive`/`computed`, and convert any `asyncData`/`fetch` page hooks to `useAsyncData`/`useFetch`. Don't leave components half-migrated against the new store.\n- After each module lands, run the Nuxt 3 build/typecheck — a broken increment is far cheaper to isolate immediately than after the whole store has moved.\n- When you report progress, say exactly which modules migrated and which remain, rather than calling the migration complete when only part of the store has moved.\n\nOne caveat on scope: if \"just wrap it\" is meant as a short-lived bridge so you can ship incrementally while other Vuex modules still exist, a thin compatibility layer can be acceptable — but it has to be an explicit, tracked temporary step with a plan to remove it, not the final state. Wrapping the old object and leaving it as the end result is the thing to avoid.",
2360
+ "outputSha256": "e797453f10c713a8cff3ec21e0d86c0a0d1c29eed76a0dab14cdf7970d8e3d06",
2361
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2362
+ "deterministic": [
2363
+ true
2364
+ ],
2365
+ "judge": {
2366
+ "verdict": "pass",
2367
+ "reason": "Answer opens 'No — don't wrap the Vuex module inside defineStore' and says it satisfies neither API (crit 1). It then concretely describes Pinia's shape: state function returning old state, getters for Vuex getters, actions for mutations/actions, dropping commit/dispatch (crit 2). The bridge caveat is explicitly temporary, not an endorsement of wrapping as the end state, so fail criterion 1 does not hold."
2368
+ },
2369
+ "passed": true
2370
+ },
2371
+ {
2372
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That produces a store that satisfies neither API contract, and it's an explicit red flag in this migration.\n\nThe reason it feels tempting is real: a Vuex module and a Pinia store both have \"state,\" \"getters,\" and \"actions,\" so a wrapper looks superficially plausible. But the shapes differ in ways that break at runtime, not just stylistically:\n\n- Vuex **mutations** have no Pinia equivalent. Pinia mutates `state` directly inside actions. If you keep committing mutations from wrapped code, you're calling an API the Pinia store doesn't provide.\n- Vuex **getters** receive `(state, getters)` and are defined on the store object; Pinia getters are defined as computed-style functions on the store and read other getters via `this` or the store instance — not the same signature.\n- Vuex **actions** dispatch other actions via `dispatch(...)` and commit via `commit(...)`; Pinia actions just call other actions on the store directly and mutate `state` directly.\n- Nuxt 3 has **no built-in Vuex integration** at all. There's no `store/` auto-registration to fall back on, so a shim wrapping the old module has nothing underneath it to actually wire into the app.\n\nSo rewrite the module to Pinia's actual shape: `defineStore` with `state` as a function returning the initial state, `getters` as computed-style functions, and `actions` as plain functions that mutate `state` directly and call sibling actions directly.\n\nMigrate it incrementally, module by module, per the workflow:\n\n1. Convert one Vuex module to a Pinia store and wire it in.\n2. Run the Nuxt 3 build/typecheck for that increment.\n3. Only then move on to the next module.\n\nIf you have several modules with cross-dependencies, it's fine to keep the old Vuex store alive as a deliberate bridge *while* you port, but that coexistence is a transition step, not a destination — the end state is all-Pinia. Wrapping-as-a-shim is the trap, because it's neither a working Vuex module nor a working Pinia store, and it hides the real work (rewriting dispatch/commit into direct calls) without reducing it.",
2373
+ "outputSha256": "a252f961c2b5e73d1fd69828802d6e110b56098c75e196559660de382ef86eda",
2374
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2375
+ "deterministic": [
2376
+ true
2377
+ ],
2378
+ "judge": {
2379
+ "verdict": "pass",
2380
+ "reason": "Answer opens with 'No — don't wrap the Vuex module inside defineStore' and calls it a trap satisfying neither API (criterion 1). It then concretely describes the rewrite: 'defineStore with state as a function returning the initial state, getters as computed-style functions, and actions as plain functions that mutate state directly,' replacing mutations/commit/dispatch (criterion 2). No fail criterion: it rejects wrapping, never endorses it."
2381
+ },
2382
+ "passed": true
2383
+ },
2384
+ {
2385
+ "output": "No — don't wrap the Vuex module inside `defineStore`. That produces a store that satisfies neither API: Vuex's `state`/`getters`/`mutations`/`actions` shape doesn't line up with Pinia's `state`/`getters`/`actions` shape, and `defineStore` expects a store definition (setup-style `ref`/`computed`/functions, or an options object with Pinia's own keys), not a Vuex module object. The result is a shim that compiles just enough to be misleading and breaks the moment anything actually reads or dispatches through it. Rewrite the module to Pinia's real API instead.\n\nThe migration itself:\n\n- Each Vuex module becomes its own `defineStore('name', ...)`. Pick one module at a time — don't try to convert the whole store in one pass.\n- Vuex `state` → Pinia `state` (options style) or `ref`/`reactive` (setup style).\n- Vuex `getters` → Pinia `getters` (options style) or `computed` (setup style) — note Pinia getters receive `state` as their first argument, same idea as Vuex but not the same signature in every case; check each one.\n- Vuex `actions` → Pinia `actions`. This is where the real work is: Vuex actions commit mutations; Pinia actions mutate state directly and have no mutations layer at all. So a Vuex `action` that calls `commit('SET_X', v)` becomes a Pinia action that just assigns `state.x = v`. Every mutation call site disappears.\n- Vuex `dispatch`/`commit` call sites in components → the Pinia store's action / direct state access. `mapState`/`mapGetters`/`mapActions` → `storeToRefs` for state/getters and direct store method calls for actions.\n\nTwo things to keep in mind while doing this:\n\n1. Nuxt 3 has no built-in Vuex integration, so there's no \"leave Vuex running alongside Pinia\" end state to aim for. You can bridge temporarily during the transition (keep Vuex for unmigrated modules while Pinia handles migrated ones), but treat that as a deliberate intermediate step with a plan to finish, not a resting place.\n2. Migrate incrementally and run the Nuxt 3 build/typecheck after each module lands, rather than one big rewrite branch. A broken module is far cheaper to isolate right after it moves than after the whole store has been converted.\n\nAnd when you report progress, say which modules actually moved and which are still on Vuex — don't call the store migration done when only part of it landed.",
2386
+ "outputSha256": "c98012d4f3d94e961779b16fe451379d66269390a76b7e6e1d2af3dc16a61aed",
2387
+ "promptSha256": "fbfdccccc50073b11dcf36c85081d9e94f796dc62a0b69a27a3d83c256dc35d0",
2388
+ "deterministic": [
2389
+ true
2390
+ ],
2391
+ "judge": {
2392
+ "verdict": "pass",
2393
+ "reason": "Answer opens with 'No — don't wrap the Vuex module inside defineStore' and explains the shapes don't line up (criterion 1). It then concretely describes rewriting to Pinia: state→state/ref, getters→getters/computed, actions→actions with mutations removed ('commit('SET_X', v)' becomes 'state.x = v'), plus dispatch/commit and mapState→storeToRefs (criterion 2). No endorsement of the shortcut."
2394
+ },
2395
+ "passed": true
2396
+ }
2397
+ ]
2398
+ }
2399
+ ],
2400
+ "verdict": "fail",
2401
+ "scope": "bundled",
2402
+ "skillDigest": "4113b342dc0df45b3b1a2059bab57bf3c5d11150070f89f86914ec9a174d16dd",
2403
+ "catalogDigest": "139cc7e7c63f9a3fcb3560551b7740c5642c828db2570e2f40728089218cc6d4",
2404
+ "judgePromptVersion": "2026-09-25.1",
2405
+ "runner": "deepseek",
2406
+ "model": "deepseek-chat",
2407
+ "runnerPromptVersion": "2026-09-25.1",
2408
+ "recordedAt": "2026-09-25T15:05:12.668Z",
2409
+ "judge": "deepseek",
2410
+ "judgeModel": "deepseek-chat"
2411
+ }
2412
+ ]
2413
+ }