@polymorphism-tech/morph-spec 4.10.8 → 4.10.10

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 (116) hide show
  1. package/CLAUDE.md +3 -3
  2. package/LICENSE +71 -71
  3. package/README.md +2 -2
  4. package/bin/morph-spec.js +22 -0
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/agents/README.md +14 -14
  9. package/framework/agents/architecture/standards-architect.md +159 -159
  10. package/framework/agents/infrastructure/azure-architect.md +149 -149
  11. package/framework/agents/infrastructure/infra-architect.md +48 -48
  12. package/framework/hooks/README.md +204 -204
  13. package/framework/hooks/shared/phase-utils.js +139 -139
  14. package/framework/rules/csharp-standards.md +13 -13
  15. package/framework/rules/frontend-standards.md +13 -13
  16. package/framework/rules/infrastructure-standards.md +16 -16
  17. package/framework/rules/testing-standards.md +14 -14
  18. package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
  19. package/framework/skills/level-0-meta/morph-init/SKILL.md +57 -9
  20. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +1 -1
  21. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +1 -1
  22. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +1 -1
  23. package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
  24. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +1 -1
  25. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
  26. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +1 -1
  27. package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
  28. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
  29. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +49 -16
  30. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  31. package/framework/standards/STANDARDS.json +2338 -2041
  32. package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
  33. package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
  34. package/framework/templates/.idea/morph-templates.xml +92 -0
  35. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  36. package/framework/templates/README.md +814 -814
  37. package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
  38. package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
  39. package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
  40. package/framework/templates/code/dotnet/contracts/README.md +74 -74
  41. package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
  42. package/framework/templates/code/dotnet/frontend/component.razor +239 -239
  43. package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
  44. package/framework/templates/code/dotnet/jobs/job.cs +171 -171
  45. package/framework/templates/code/dotnet/test.cs +239 -239
  46. package/framework/templates/code/sql/rls-policy.sql +57 -57
  47. package/framework/templates/code/sql/supabase-migration.sql +100 -100
  48. package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
  49. package/framework/templates/code/typescript/contracts.ts +168 -168
  50. package/framework/templates/context/CONTEXT.md +181 -181
  51. package/framework/templates/docs/proposal.md +182 -182
  52. package/framework/templates/docs/user-stories.md +34 -34
  53. package/framework/templates/examples/design-system-examples.md +357 -357
  54. package/framework/templates/examples/spec-examples.md +90 -90
  55. package/framework/templates/feature/decisions.md +187 -187
  56. package/framework/templates/feature/recap.md +146 -146
  57. package/framework/templates/feature/tasks.md +199 -199
  58. package/framework/templates/infrastructure/azure/README.md +286 -286
  59. package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
  60. package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
  61. package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
  62. package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
  63. package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
  64. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  65. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  66. package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
  67. package/package.json +1 -1
  68. package/src/commands/agents/dispatch-agents.js +17 -0
  69. package/src/commands/agents/index.js +13 -13
  70. package/src/commands/agents/project-metrics.js +74 -0
  71. package/src/commands/project/install-plugin-cmd.js +28 -28
  72. package/src/commands/project/setup-infra-cmd.js +12 -12
  73. package/src/commands/standards/context-budget.js +89 -0
  74. package/src/commands/tasks/index.js +4 -4
  75. package/src/commands/templates/index.js +5 -5
  76. package/src/commands/utils/index.js +3 -3
  77. package/src/commands/validation/index.js +4 -4
  78. package/src/core/state/phase-state-machine.js +217 -217
  79. package/src/lib/agents/metrics-aggregator.js +56 -0
  80. package/src/lib/detectors/design-system-detector.js +73 -31
  81. package/src/lib/generators/design-system-generator.js +298 -298
  82. package/src/lib/generators/index.js +10 -10
  83. package/src/lib/generators/metadata-extractor.js +387 -387
  84. package/src/lib/stack-filter.js +65 -65
  85. package/src/lib/standards/digest-builder.js +57 -3
  86. package/src/lib/standards/index.js +7 -7
  87. package/src/lib/standards/standards-context-injector.js +302 -302
  88. package/src/lib/validators/architecture/architecture-validator.js +387 -387
  89. package/src/lib/validators/architecture/index.js +7 -7
  90. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
  91. package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
  92. package/src/lib/validators/blazor/blazor-validator.js +369 -369
  93. package/src/lib/validators/blazor/index.js +9 -9
  94. package/src/lib/validators/content/content-validator.js +287 -287
  95. package/src/lib/validators/content/index.js +7 -7
  96. package/src/lib/validators/contracts/index.js +7 -7
  97. package/src/lib/validators/css/css-validator.js +352 -352
  98. package/src/lib/validators/css/index.js +7 -7
  99. package/src/lib/validators/design-system/design-system-validator.js +231 -231
  100. package/src/lib/validators/design-system/index.js +7 -7
  101. package/src/lib/validators/nextjs/index.js +6 -6
  102. package/src/lib/validators/nextjs/next-component-validator.js +183 -183
  103. package/src/lib/validators/packages/index.js +7 -7
  104. package/src/lib/validators/packages/package-validator.js +360 -360
  105. package/src/lib/validators/shared/index.js +12 -12
  106. package/src/lib/validators/shared/issue-counter.js +18 -18
  107. package/src/lib/validators/shared/result-formatter.js +124 -124
  108. package/src/lib/validators/ui/index.js +7 -7
  109. package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
  110. package/src/scripts/global-install.js +58 -58
  111. package/src/scripts/install-plugin.js +126 -126
  112. package/src/utils/claude-md-injector.js +90 -90
  113. package/src/utils/file-copier.js +200 -200
  114. package/src/utils/logger.js +32 -32
  115. package/src/utils/skills-installer.js +178 -178
  116. package/src/utils/version-checker.js +175 -175
@@ -1,254 +1,254 @@
1
- # Code Review Report — User Management Feature
2
-
3
- > Example of a well-structured Next.js code review output. Filled-in reference — not a template.
4
-
5
- **Scope:** `src/features/users/` + `src/app/(dashboard)/users/page.tsx`
6
- **Date:** 2026-02-23
7
- **Reviewer:** code-review-nextjs skill
8
-
9
- ---
10
-
11
- ## Automated Scan Results
12
-
13
- ```bash
14
- $ node scripts/scan-nextjs.mjs src/features/users/
15
-
16
- CRITICAL (1)
17
- features/users/components/UserList.tsx:1 — React hooks used (useState) without 'use client'
18
-
19
- HIGH/MEDIUM (2)
20
- features/users/components/UserList.tsx — PascalCase filename (should be user-list.tsx)
21
- features/users/components/user-form.tsx — 'use client' directive present but no interactivity detected
22
-
23
- Summary: 8 files scanned | 3 issues (1 critical, 2 high/medium)
24
- ```
25
-
26
- ---
27
-
28
- ## Findings by Severity
29
-
30
- | Severity | Count |
31
- |----------|-------|
32
- | CRITICAL | 2 |
33
- | HIGH | 3 |
34
- | MEDIUM | 2 |
35
- | LOW | 1 |
36
-
37
- ---
38
-
39
- ### [CRITICAL] Missing 'use client' on interactive component
40
-
41
- **File:** `src/features/users/components/UserList.tsx:1`
42
-
43
- **Current code:**
44
- ```tsx
45
- import { useState } from 'react';
46
-
47
- export function UserList() {
48
- const [selected, setSelected] = useState<string | null>(null);
49
- return <div onClick={() => setSelected('test')}>...</div>;
50
- }
51
- ```
52
-
53
- **Suggested fix:**
54
- ```tsx
55
- 'use client';
56
-
57
- import { useState } from 'react';
58
- // ... rest unchanged
59
- ```
60
-
61
- **Why:** React hooks (`useState`) require `'use client'`. Without it, Next.js treats this as a Server Component and the `useState` call fails at runtime.
62
-
63
- ---
64
-
65
- ### [CRITICAL] useEffect for data fetching
66
-
67
- **File:** `src/features/users/components/user-list.tsx:8`
68
-
69
- **Current code:**
70
- ```tsx
71
- 'use client';
72
- import { useEffect, useState } from 'react';
73
-
74
- export function UserList() {
75
- const [users, setUsers] = useState([]);
76
- useEffect(() => {
77
- fetch('/api/users').then(r => r.json()).then(setUsers);
78
- }, []);
79
- return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
80
- }
81
- ```
82
-
83
- **Suggested fix:**
84
- ```tsx
85
- // Option A — Server Component (no 'use client' needed)
86
- // app/(dashboard)/users/page.tsx
87
- async function getUsers() {
88
- const res = await fetch(`${process.env.API_URL}/api/users`, { next: { revalidate: 30 } });
89
- return res.json();
90
- }
91
- export default async function UsersPage() {
92
- const users = await getUsers();
93
- return <UserList initialData={users} />;
94
- }
95
-
96
- // Option B — TanStack Query (when client interactivity needed)
97
- 'use client';
98
- import { useUsers } from '@/features/users/hooks/use-users';
99
- export function UserList() {
100
- const { data: users = [], isLoading } = useUsers();
101
- if (isLoading) return <div>Loading...</div>;
102
- return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
103
- }
104
- ```
105
-
106
- **Why:** `useEffect` + `useState` for fetching: (1) doubles renders, (2) no caching, (3) no SSR, (4) no error/loading states, (5) can't be cancelled. TanStack Query or Server Components solve all five.
107
-
108
- ---
109
-
110
- ### [HIGH] PascalCase file name
111
-
112
- **File:** `src/features/users/components/UserList.tsx`
113
-
114
- **Suggested fix:** Rename to `user-list.tsx`
115
-
116
- **Why:** Linux servers are case-sensitive. `UserList.tsx` and `user-list.tsx` are different files on the server but the same on macOS/Windows, causing import failures in production.
117
-
118
- ---
119
-
120
- ### [HIGH] Default export on non-page component
121
-
122
- **File:** `src/features/users/components/user-card.tsx:3`
123
-
124
- **Current code:**
125
- ```tsx
126
- export default function UserCard({ user }: { user: User }) {
127
- ```
128
-
129
- **Suggested fix:**
130
- ```tsx
131
- export function UserCard({ user }: { user: User }) {
132
- ```
133
-
134
- **Why:** Named exports are refactor-safe — IDEs track renames automatically. Default exports are anonymous in imports (`import X from './user-card'` allows `X` to be anything).
135
-
136
- ---
137
-
138
- ### [HIGH] Type defined manually instead of deriving from Zod schema
139
-
140
- **File:** `src/features/users/types/user.types.ts:1`
141
-
142
- **Current code:**
143
- ```ts
144
- export type User = {
145
- id: string;
146
- name: string;
147
- email: string;
148
- role: 'admin' | 'user';
149
- };
150
- ```
151
-
152
- **Suggested fix:**
153
- ```ts
154
- // user.schemas.ts — single source of truth
155
- export const userSchema = z.object({
156
- id: z.string(),
157
- name: z.string(),
158
- email: z.string().email(),
159
- role: z.enum(['admin', 'user']),
160
- });
161
-
162
- // user.types.ts — derived, never written manually
163
- export type User = z.infer<typeof userSchema>;
164
- ```
165
-
166
- **Why:** Manually written types drift from API responses. `z.infer<>` ensures the TypeScript type is always in sync with the runtime validation.
167
-
168
- ---
169
-
170
- ### [MEDIUM] Deep path import instead of feature index
171
-
172
- **File:** `src/app/(dashboard)/users/page.tsx:2`
173
-
174
- **Current code:**
175
- ```tsx
176
- import { UserList } from '@/features/users/components/user-list';
177
- ```
178
-
179
- **Suggested fix:**
180
- ```tsx
181
- import { UserList } from '@/features/users';
182
- ```
183
-
184
- **Why:** Deep path imports expose internal structure. When `user-list.tsx` moves or is renamed, every consumer breaks. The feature index is the stable public API.
185
-
186
- ---
187
-
188
- ### [MEDIUM] Query key not using factory pattern
189
-
190
- **File:** `src/features/users/hooks/use-users.ts:8`
191
-
192
- **Current code:**
193
- ```ts
194
- return useQuery({
195
- queryKey: ['users'],
196
- ```
197
-
198
- **Suggested fix:**
199
- ```ts
200
- // query-keys.ts
201
- export const userKeys = {
202
- all: ['users'] as const,
203
- lists: () => [...userKeys.all, 'list'] as const,
204
- };
205
-
206
- // use-users.ts
207
- return useQuery({
208
- queryKey: userKeys.lists(),
209
- ```
210
-
211
- **Why:** Hardcoded `['users']` cannot be selectively invalidated. `queryClient.invalidateQueries({ queryKey: userKeys.lists() })` correctly invalidates only list queries, not detail queries with the same prefix.
212
-
213
- ---
214
-
215
- ### [LOW] Test uses fireEvent instead of userEvent
216
-
217
- **File:** `src/features/users/components/user-card.test.tsx:14`
218
-
219
- **Current code:**
220
- ```tsx
221
- fireEvent.click(screen.getByRole('button', { name: /edit/i }));
222
- ```
223
-
224
- **Suggested fix:**
225
- ```tsx
226
- await userEvent.click(screen.getByRole('button', { name: /edit/i }));
227
- ```
228
-
229
- **Why:** `fireEvent.click` dispatches a synthetic event. `userEvent.click` simulates the full browser interaction sequence (pointerdown, mousedown, focus, click, etc.) — closer to real user behaviour.
230
-
231
- ---
232
-
233
- ## Top Priorities
234
-
235
- 1. `UserList.tsx:1` — CRITICAL missing `'use client'` (2 min fix)
236
- 2. `user-list.tsx:8` — CRITICAL useEffect fetch → useQuery (15 min)
237
- 3. `UserList.tsx` — HIGH rename to `user-list.tsx` (2 min)
238
- 4. `user-card.tsx:3` — HIGH named export (2 min)
239
- 5. `user.types.ts:1` — HIGH derive from Zod (10 min)
240
-
241
- ---
242
-
243
- ## Passed Checks ✅
244
-
245
- - `components/ui/` files untouched
246
- - `zodResolver` correctly wired in `user-form.tsx`
247
- - `isPending` used (v5 correct — not `isLoading`)
248
- - `loading.tsx` and `error.tsx` present in `app/(dashboard)/users/`
249
- - Feature has `index.ts` re-exporting public API
250
- - Test file co-located with component
251
-
252
- ---
253
-
254
- *MORPH-SPEC by Polymorphism Tech*
1
+ # Code Review Report — User Management Feature
2
+
3
+ > Example of a well-structured Next.js code review output. Filled-in reference — not a template.
4
+
5
+ **Scope:** `src/features/users/` + `src/app/(dashboard)/users/page.tsx`
6
+ **Date:** 2026-02-23
7
+ **Reviewer:** code-review-nextjs skill
8
+
9
+ ---
10
+
11
+ ## Automated Scan Results
12
+
13
+ ```bash
14
+ $ node scripts/scan-nextjs.mjs src/features/users/
15
+
16
+ CRITICAL (1)
17
+ features/users/components/UserList.tsx:1 — React hooks used (useState) without 'use client'
18
+
19
+ HIGH/MEDIUM (2)
20
+ features/users/components/UserList.tsx — PascalCase filename (should be user-list.tsx)
21
+ features/users/components/user-form.tsx — 'use client' directive present but no interactivity detected
22
+
23
+ Summary: 8 files scanned | 3 issues (1 critical, 2 high/medium)
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Findings by Severity
29
+
30
+ | Severity | Count |
31
+ |----------|-------|
32
+ | CRITICAL | 2 |
33
+ | HIGH | 3 |
34
+ | MEDIUM | 2 |
35
+ | LOW | 1 |
36
+
37
+ ---
38
+
39
+ ### [CRITICAL] Missing 'use client' on interactive component
40
+
41
+ **File:** `src/features/users/components/UserList.tsx:1`
42
+
43
+ **Current code:**
44
+ ```tsx
45
+ import { useState } from 'react';
46
+
47
+ export function UserList() {
48
+ const [selected, setSelected] = useState<string | null>(null);
49
+ return <div onClick={() => setSelected('test')}>...</div>;
50
+ }
51
+ ```
52
+
53
+ **Suggested fix:**
54
+ ```tsx
55
+ 'use client';
56
+
57
+ import { useState } from 'react';
58
+ // ... rest unchanged
59
+ ```
60
+
61
+ **Why:** React hooks (`useState`) require `'use client'`. Without it, Next.js treats this as a Server Component and the `useState` call fails at runtime.
62
+
63
+ ---
64
+
65
+ ### [CRITICAL] useEffect for data fetching
66
+
67
+ **File:** `src/features/users/components/user-list.tsx:8`
68
+
69
+ **Current code:**
70
+ ```tsx
71
+ 'use client';
72
+ import { useEffect, useState } from 'react';
73
+
74
+ export function UserList() {
75
+ const [users, setUsers] = useState([]);
76
+ useEffect(() => {
77
+ fetch('/api/users').then(r => r.json()).then(setUsers);
78
+ }, []);
79
+ return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
80
+ }
81
+ ```
82
+
83
+ **Suggested fix:**
84
+ ```tsx
85
+ // Option A — Server Component (no 'use client' needed)
86
+ // app/(dashboard)/users/page.tsx
87
+ async function getUsers() {
88
+ const res = await fetch(`${process.env.API_URL}/api/users`, { next: { revalidate: 30 } });
89
+ return res.json();
90
+ }
91
+ export default async function UsersPage() {
92
+ const users = await getUsers();
93
+ return <UserList initialData={users} />;
94
+ }
95
+
96
+ // Option B — TanStack Query (when client interactivity needed)
97
+ 'use client';
98
+ import { useUsers } from '@/features/users/hooks/use-users';
99
+ export function UserList() {
100
+ const { data: users = [], isLoading } = useUsers();
101
+ if (isLoading) return <div>Loading...</div>;
102
+ return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
103
+ }
104
+ ```
105
+
106
+ **Why:** `useEffect` + `useState` for fetching: (1) doubles renders, (2) no caching, (3) no SSR, (4) no error/loading states, (5) can't be cancelled. TanStack Query or Server Components solve all five.
107
+
108
+ ---
109
+
110
+ ### [HIGH] PascalCase file name
111
+
112
+ **File:** `src/features/users/components/UserList.tsx`
113
+
114
+ **Suggested fix:** Rename to `user-list.tsx`
115
+
116
+ **Why:** Linux servers are case-sensitive. `UserList.tsx` and `user-list.tsx` are different files on the server but the same on macOS/Windows, causing import failures in production.
117
+
118
+ ---
119
+
120
+ ### [HIGH] Default export on non-page component
121
+
122
+ **File:** `src/features/users/components/user-card.tsx:3`
123
+
124
+ **Current code:**
125
+ ```tsx
126
+ export default function UserCard({ user }: { user: User }) {
127
+ ```
128
+
129
+ **Suggested fix:**
130
+ ```tsx
131
+ export function UserCard({ user }: { user: User }) {
132
+ ```
133
+
134
+ **Why:** Named exports are refactor-safe — IDEs track renames automatically. Default exports are anonymous in imports (`import X from './user-card'` allows `X` to be anything).
135
+
136
+ ---
137
+
138
+ ### [HIGH] Type defined manually instead of deriving from Zod schema
139
+
140
+ **File:** `src/features/users/types/user.types.ts:1`
141
+
142
+ **Current code:**
143
+ ```ts
144
+ export type User = {
145
+ id: string;
146
+ name: string;
147
+ email: string;
148
+ role: 'admin' | 'user';
149
+ };
150
+ ```
151
+
152
+ **Suggested fix:**
153
+ ```ts
154
+ // user.schemas.ts — single source of truth
155
+ export const userSchema = z.object({
156
+ id: z.string(),
157
+ name: z.string(),
158
+ email: z.string().email(),
159
+ role: z.enum(['admin', 'user']),
160
+ });
161
+
162
+ // user.types.ts — derived, never written manually
163
+ export type User = z.infer<typeof userSchema>;
164
+ ```
165
+
166
+ **Why:** Manually written types drift from API responses. `z.infer<>` ensures the TypeScript type is always in sync with the runtime validation.
167
+
168
+ ---
169
+
170
+ ### [MEDIUM] Deep path import instead of feature index
171
+
172
+ **File:** `src/app/(dashboard)/users/page.tsx:2`
173
+
174
+ **Current code:**
175
+ ```tsx
176
+ import { UserList } from '@/features/users/components/user-list';
177
+ ```
178
+
179
+ **Suggested fix:**
180
+ ```tsx
181
+ import { UserList } from '@/features/users';
182
+ ```
183
+
184
+ **Why:** Deep path imports expose internal structure. When `user-list.tsx` moves or is renamed, every consumer breaks. The feature index is the stable public API.
185
+
186
+ ---
187
+
188
+ ### [MEDIUM] Query key not using factory pattern
189
+
190
+ **File:** `src/features/users/hooks/use-users.ts:8`
191
+
192
+ **Current code:**
193
+ ```ts
194
+ return useQuery({
195
+ queryKey: ['users'],
196
+ ```
197
+
198
+ **Suggested fix:**
199
+ ```ts
200
+ // query-keys.ts
201
+ export const userKeys = {
202
+ all: ['users'] as const,
203
+ lists: () => [...userKeys.all, 'list'] as const,
204
+ };
205
+
206
+ // use-users.ts
207
+ return useQuery({
208
+ queryKey: userKeys.lists(),
209
+ ```
210
+
211
+ **Why:** Hardcoded `['users']` cannot be selectively invalidated. `queryClient.invalidateQueries({ queryKey: userKeys.lists() })` correctly invalidates only list queries, not detail queries with the same prefix.
212
+
213
+ ---
214
+
215
+ ### [LOW] Test uses fireEvent instead of userEvent
216
+
217
+ **File:** `src/features/users/components/user-card.test.tsx:14`
218
+
219
+ **Current code:**
220
+ ```tsx
221
+ fireEvent.click(screen.getByRole('button', { name: /edit/i }));
222
+ ```
223
+
224
+ **Suggested fix:**
225
+ ```tsx
226
+ await userEvent.click(screen.getByRole('button', { name: /edit/i }));
227
+ ```
228
+
229
+ **Why:** `fireEvent.click` dispatches a synthetic event. `userEvent.click` simulates the full browser interaction sequence (pointerdown, mousedown, focus, click, etc.) — closer to real user behaviour.
230
+
231
+ ---
232
+
233
+ ## Top Priorities
234
+
235
+ 1. `UserList.tsx:1` — CRITICAL missing `'use client'` (2 min fix)
236
+ 2. `user-list.tsx:8` — CRITICAL useEffect fetch → useQuery (15 min)
237
+ 3. `UserList.tsx` — HIGH rename to `user-list.tsx` (2 min)
238
+ 4. `user-card.tsx:3` — HIGH named export (2 min)
239
+ 5. `user.types.ts:1` — HIGH derive from Zod (10 min)
240
+
241
+ ---
242
+
243
+ ## Passed Checks ✅
244
+
245
+ - `components/ui/` files untouched
246
+ - `zodResolver` correctly wired in `user-form.tsx`
247
+ - `isPending` used (v5 correct — not `isLoading`)
248
+ - `loading.tsx` and `error.tsx` present in `app/(dashboard)/users/`
249
+ - Feature has `index.ts` re-exporting public API
250
+ - Test file co-located with component
251
+
252
+ ---
253
+
254
+ *MORPH-SPEC by Polymorphism Tech*
@@ -155,24 +155,70 @@ Do **not** ask about technology already confirmed by file evidence.
155
155
 
156
156
  ## Step 4.5 — Scaffold Design System
157
157
 
158
- Check if the project already has a design system file:
158
+ ### 4.5.0 — Check if DS already exists
159
159
 
160
160
  ```
161
161
  Read: .morph/context/design-system.md
162
162
  ```
163
163
 
164
164
  **If the file EXISTS with non-TODO content in the Aesthetic Direction section:**
165
- → Skip this step. Print: `✓ Design system found — .morph/context/design-system.md`
165
+ → Skip steps 4.5.1–4.5.2. Print: `✓ Design system found — .morph/context/design-system.md`
166
166
 
167
- **If the file does NOT EXIST or is empty:**
167
+ ### 4.5.1 — ALWAYS scan for existing CSS tokens
168
168
 
169
- 1. **Copy the template:**
169
+ Search for existing CSS custom properties across the project's frontend directories.
170
+
171
+ Glob these patterns (exclude `node_modules/`, `.next/`, `dist/`, `build/`):
172
+ ```
173
+ wwwroot/**/*.css
174
+ wwwroot/**/*.scss
175
+ styles/**/*.css
176
+ src/styles/**/*.css
177
+ src/**/*.css
178
+ src/**/*.scss
179
+ src/**/*.module.css
180
+ public/**/*.css
181
+ ```
182
+
183
+ For each CSS/SCSS file found, Grep for `:root {` blocks containing `--` custom properties.
184
+
185
+ Track:
186
+ - `cssTokensFound` (bool)
187
+ - `tokenCount` (int — total number of `--variable` lines found)
188
+ - `sourceFiles` (array of file paths that had tokens)
189
+
190
+ ### 4.5.2 — Decision tree
191
+
192
+ **Case A: CSS tokens found (existing project)**
193
+
194
+ 1. Create `.morph/context/design-system.md` from scanned tokens:
195
+ - `## Color Tokens` → `:root { }` block with all found color variables (preserve exact variable names from CSS)
196
+ - `## Typography` → found font-family values (map to `--font-display` / `--font-body` by variable name heuristic)
197
+ - `## Spacing Tokens` → found spacing variables
198
+ - `## Aesthetic Direction` → `#TODO` (requires user judgment — fill in during `/morph-phase-uiux`)
199
+ - `## Changelog` → `- [today's date] [morph-init] Auto-generated from CSS scan ([N] tokens from [files])`
200
+
201
+ 2. Skip the `AskUserQuestion` for direction/brand color — the project has established colors
202
+
203
+ 3. Print:
204
+ ```
205
+ ✓ Design system scaffolded from existing CSS
206
+ Source: [file list]
207
+ Tokens: [N] colors, [M] font families, [K] spacing values
208
+ Note: Aesthetic Direction is TODO — fill in during /morph-phase-uiux
209
+ ```
210
+
211
+ 4. Continue to Step 5.
212
+
213
+ **Case B: No CSS tokens found (greenfield project)**
214
+
215
+ 1. Copy the template:
170
216
  ```bash
171
217
  cp .morph/framework/templates/context/design-system.md .morph/context/design-system.md
172
218
  ```
173
219
  *(The template is at `framework/templates/context/design-system.md` in the installed framework)*
174
220
 
175
- 2. **Ask 3 quick questions** using `AskUserQuestion`:
221
+ 2. Ask 3 quick questions using `AskUserQuestion`:
176
222
 
177
223
  ```json
178
224
  {
@@ -216,15 +262,15 @@ Read: .morph/context/design-system.md
216
262
  }
217
263
  ```
218
264
 
219
- 4. **Fill in the design-system.md** with user responses:
265
+ 4. Fill in the design-system.md with user responses:
220
266
 
221
267
  | User answered | What to update |
222
268
  |--------------|----------------|
223
- | Direction chosen | Replace `[Minimal Refined / Editorial / ...]` with chosen direction. Update Font Pair with the correct fonts from this mapping: Minimal Refined → Cormorant Garamond + DM Sans; Editorial → Playfair Display + Source Sans 3; Soft Professional → Nunito + Inter; Industrial → JetBrains Mono + IBM Plex Sans; Modern Luxury → Bodoni Moda + Sora |
224
- | Color chosen | Replace `--color-primary: #TODO` with a reasonable hex for that color family (deep blue → #1e3a5f, rich green → #065f46, warm orange → #c2410c, purple → #6d28d9, black → #0a0a0a). Add a note: "Adjust exact values to match your brand" |
269
+ | Direction chosen | Replace `[Minimal Refined / Editorial / ...]` with chosen direction. Update Font Pair with the correct fonts: Minimal Refined → Cormorant Garamond + DM Sans; Editorial → Playfair Display + Source Sans 3; Soft Professional → Nunito + Inter; Industrial → JetBrains Mono + IBM Plex Sans; Modern Luxury → Bodoni Moda + Sora |
270
+ | Color chosen | Replace `--color-primary: #TODO` with a reasonable hex for that color family: deep blue → #1e3a5f, rich green → #065f46, warm orange → #c2410c, purple → #6d28d9, black → #0a0a0a. Add note: "Adjust exact values to match your brand" |
225
271
  | "Skip for now" | Leave all TODOs in place |
226
272
 
227
- 5. **Update the Changelog** in design-system.md:
273
+ 5. Update the Changelog in design-system.md:
228
274
  Add entry: `- [today's date] [morph-init] Initial scaffold from morph-init`
229
275
 
230
276
  6. Print confirmation:
@@ -234,6 +280,8 @@ Read: .morph/context/design-system.md
234
280
  Edit to refine exact token values before the UI/UX phase.
235
281
  ```
236
282
 
283
+ 7. Continue to Step 5.
284
+
237
285
  ---
238
286
 
239
287
  ## Step 5 — Generate `context/README.md`
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 3 (Clarify). Iterative clarification loop driven b
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
7
- cliVersion: "4.10.8"
7
+ cliVersion: "4.10.10"
8
8
  ---
9
9
 
10
10
  # MORPH Clarify — Phase 3
@@ -3,7 +3,7 @@ name: morph-phase-codebase-analysis
3
3
  description: MORPH-SPEC Design sub-phase that analyzes existing codebase and database schema, producing schema-analysis.md with real column names, types, relationships, and field mismatches. Use at the start of Design phase before generating contracts.cs to prevent incorrect field names or types.
4
4
  user-invocable: false
5
5
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion
6
- cliVersion: "4.10.8"
6
+ cliVersion: "4.10.10"
7
7
  ---
8
8
 
9
9
  # MORPH Codebase Analysis - Design Sub-phase
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 2 (Design). Schema-first interactive design: reads
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, Agent
7
- cliVersion: "4.10.8"
7
+ cliVersion: "4.10.10"
8
8
  ---
9
9
 
10
10
  # MORPH Design — Phase 2