@polymorphism-tech/morph-spec 4.10.9 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -3
- package/LICENSE +71 -71
- package/README.md +5 -11
- package/bin/morph-spec.js +23 -1
- package/bin/task-manager.js +5 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +2 -8
- package/framework/agents/README.md +14 -14
- package/framework/agents/architecture/standards-architect.md +159 -159
- package/framework/agents/frontend/css-specialist.md +1 -1
- package/framework/agents/infrastructure/azure-architect.md +149 -149
- package/framework/agents/infrastructure/azure-deploy-specialist.md +4 -4
- package/framework/agents/infrastructure/infra-architect.md +48 -48
- package/framework/commands/morph-apply.md +18 -18
- package/framework/commands/morph-preflight.md +3 -3
- package/framework/commands/morph-proposal.md +15 -15
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +1 -1
- package/framework/hooks/README.md +204 -204
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +2 -2
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +3 -3
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +2 -2
- package/framework/hooks/git/pre-commit/agents.sh +1 -1
- package/framework/hooks/shared/phase-utils.js +139 -139
- package/framework/rules/csharp-standards.md +13 -13
- package/framework/rules/frontend-standards.md +13 -13
- package/framework/rules/infrastructure-standards.md +16 -16
- package/framework/rules/testing-standards.md +14 -14
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +8 -8
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +254 -254
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +11 -11
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +3 -3
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +4 -4
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +25 -25
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +8 -8
- package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +23 -23
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +38 -0
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +29 -29
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -4
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +17 -17
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +14 -0
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +16 -16
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +5 -5
- package/framework/standards/STANDARDS.json +2338 -2041
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +17 -17
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +206 -0
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/README.md +814 -814
- package/framework/templates/code/dotnet/contracts/Commands.cs +74 -74
- package/framework/templates/code/dotnet/contracts/Entities.cs +25 -25
- package/framework/templates/code/dotnet/contracts/Queries.cs +74 -74
- package/framework/templates/code/dotnet/contracts/README.md +74 -74
- package/framework/templates/code/dotnet/contracts/api-contracts.cs +173 -173
- package/framework/templates/code/dotnet/frontend/component.razor +239 -239
- package/framework/templates/code/dotnet/jobs/agent.cs +163 -163
- package/framework/templates/code/dotnet/jobs/job.cs +171 -171
- package/framework/templates/code/dotnet/test.cs +239 -239
- package/framework/templates/code/sql/rls-policy.sql +57 -57
- package/framework/templates/code/sql/supabase-migration.sql +100 -100
- package/framework/templates/code/sql/supabase-migration.template.sql +113 -113
- package/framework/templates/code/typescript/contracts.ts +168 -168
- package/framework/templates/context/CONTEXT.md +181 -181
- package/framework/templates/docs/proposal.md +182 -182
- package/framework/templates/docs/user-stories.md +34 -34
- package/framework/templates/examples/design-system-examples.md +357 -357
- package/framework/templates/examples/spec-examples.md +90 -90
- package/framework/templates/feature/decisions.md +187 -187
- package/framework/templates/feature/recap.md +146 -146
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/infrastructure/azure/README.md +286 -286
- package/framework/templates/infrastructure/azure/deploy-checklist.md +426 -426
- package/framework/templates/infrastructure/azure/parameters.dev.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.prod.json +29 -29
- package/framework/templates/infrastructure/azure/parameters.staging.json +29 -29
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +67 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +54 -54
- package/framework/workflows/README.md +18 -18
- package/framework/workflows/docs/enforcement-pipeline.md +1 -1
- package/framework/workflows/docs/full-morph.md +2 -2
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +17 -0
- package/src/commands/agents/index.js +13 -13
- package/src/commands/agents/project-metrics.js +74 -0
- package/src/commands/project/doctor.js +3 -4
- package/src/commands/project/install-plugin-cmd.js +28 -28
- package/src/commands/project/setup-infra-cmd.js +12 -12
- package/src/commands/project/status.js +8 -1
- package/src/commands/scope/escalate.js +5 -3
- package/src/commands/standards/context-budget.js +89 -0
- package/src/commands/tasks/index.js +4 -4
- package/src/commands/templates/index.js +5 -5
- package/src/commands/utils/index.js +3 -3
- package/src/commands/validation/index.js +4 -4
- package/src/commands/validation/validate-feature.js +32 -3
- package/src/core/state/phase-state-machine.js +217 -217
- package/src/lib/agents/metrics-aggregator.js +56 -0
- package/src/lib/generators/design-system-generator.js +298 -298
- package/src/lib/generators/index.js +10 -10
- package/src/lib/generators/metadata-extractor.js +387 -387
- package/src/lib/stack-filter.js +65 -65
- package/src/lib/standards/digest-builder.js +57 -3
- package/src/lib/standards/index.js +7 -7
- package/src/lib/standards/standards-context-injector.js +302 -302
- package/src/lib/validators/architecture/architecture-validator.js +387 -387
- package/src/lib/validators/architecture/index.js +7 -7
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +316 -316
- package/src/lib/validators/blazor/blazor-state-validator.js +279 -279
- package/src/lib/validators/blazor/blazor-validator.js +369 -369
- package/src/lib/validators/blazor/index.js +9 -9
- package/src/lib/validators/content/content-validator.js +287 -287
- package/src/lib/validators/content/index.js +7 -7
- package/src/lib/validators/contracts/index.js +7 -7
- package/src/lib/validators/css/css-validator.js +352 -352
- package/src/lib/validators/css/index.js +7 -7
- package/src/lib/validators/design-system/design-system-validator.js +231 -231
- package/src/lib/validators/design-system/index.js +7 -7
- package/src/lib/validators/nextjs/index.js +6 -6
- package/src/lib/validators/nextjs/next-component-validator.js +183 -183
- package/src/lib/validators/packages/index.js +7 -7
- package/src/lib/validators/packages/package-validator.js +360 -360
- package/src/lib/validators/shared/index.js +12 -12
- package/src/lib/validators/shared/issue-counter.js +18 -18
- package/src/lib/validators/shared/result-formatter.js +124 -124
- package/src/lib/validators/ui/index.js +7 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +422 -422
- package/src/scripts/global-install.js +112 -58
- package/src/scripts/install-plugin.js +126 -126
- package/src/utils/claude-md-injector.js +90 -90
- package/src/utils/file-copier.js +200 -200
- package/src/utils/logger.js +32 -32
- package/src/utils/skills-installer.js +178 -178
- package/src/utils/version-checker.js +169 -175
package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md
CHANGED
|
@@ -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*
|
|
@@ -57,7 +57,7 @@ Store `stack`, `frontendPath`, `startCommand` and the detected context — all s
|
|
|
57
57
|
## Step 1 — Design Outputs Validation (if context includes design validation)
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
|
|
60
|
+
morph-spec validate-feature $ARGUMENTS
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Verify existence and quality of the 4 mandatory outputs in `.morph/features/$ARGUMENTS/2-ui/`:
|
|
@@ -25,10 +25,10 @@ Before running any `morph-spec` command, detect how to invoke the CLI:
|
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
# Try local first (project dependency), then global
|
|
28
|
-
|
|
28
|
+
morph-spec --version
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
- If `
|
|
31
|
+
- If `morph-spec --version` succeeds → use `morph-spec` as the CLI prefix for all commands.
|
|
32
32
|
- If it fails → try `morph-spec --version` (global install). If that works → use `morph-spec`.
|
|
33
33
|
- If both fail → **STOP** and show:
|
|
34
34
|
```
|
|
@@ -36,7 +36,7 @@ npx morph-spec --version
|
|
|
36
36
|
npm install @polymorphism-tech/morph-spec
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Use the resolved CLI prefix (e.g. `
|
|
39
|
+
Use the resolved CLI prefix (e.g. `morph-spec`) for ALL bash commands in the following steps.
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
@@ -78,19 +78,19 @@ Check if `.morph/` exists in the current directory.
|
|
|
78
78
|
|
|
79
79
|
**If MISSING:**
|
|
80
80
|
```bash
|
|
81
|
-
|
|
81
|
+
morph-spec setup-infra
|
|
82
82
|
```
|
|
83
83
|
Output confirms: `✓ MORPH-SPEC infrastructure installed.`
|
|
84
84
|
|
|
85
85
|
**If EXISTS and argument is `refresh`:**
|
|
86
86
|
Re-run setup-infra to update framework files, then continue with context/config refresh:
|
|
87
87
|
```bash
|
|
88
|
-
|
|
88
|
+
morph-spec setup-infra
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
**If EXISTS and no argument:**
|
|
92
92
|
Ask: *"MORPH already initialized. Refresh context and config? (y/n)"*
|
|
93
|
-
If `y` → re-run `
|
|
93
|
+
If `y` → re-run `morph-spec setup-infra` then continue.
|
|
94
94
|
If `n` → STOP.
|
|
95
95
|
|
|
96
96
|
---
|
|
@@ -359,7 +359,7 @@ And inform the user:
|
|
|
359
359
|
To create a new VSA project:
|
|
360
360
|
dotnet new install https://github.com/polymorphism-tech/Morph_Template_VerticalSliceArchitecture
|
|
361
361
|
dotnet new vsa -n MyProject
|
|
362
|
-
cd MyProject &&
|
|
362
|
+
cd MyProject && morph-spec init
|
|
363
363
|
```
|
|
364
364
|
|
|
365
365
|
---
|
|
@@ -374,7 +374,7 @@ For each detected integration with an available MCP:
|
|
|
374
374
|
```json
|
|
375
375
|
"supabase": { "type": "http", "url": "https://mcp.supabase.com/mcp" }
|
|
376
376
|
```
|
|
377
|
-
- NO → show snippet + `
|
|
377
|
+
- NO → show snippet + `morph-spec mcp setup supabase`
|
|
378
378
|
|
|
379
379
|
**GitHub:**
|
|
380
380
|
> *"Configure GitHub MCP? Requires Docker + a `GITHUB_PERSONAL_ACCESS_TOKEN`."*
|
|
@@ -386,7 +386,7 @@ For each detected integration with an available MCP:
|
|
|
386
386
|
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "<token>" }
|
|
387
387
|
}
|
|
388
388
|
```
|
|
389
|
-
- NO → show snippet + `
|
|
389
|
+
- NO → show snippet + `morph-spec mcp setup github`
|
|
390
390
|
|
|
391
391
|
**Vercel detected** (vercel.json exists OR stack is nextjs):
|
|
392
392
|
> *"Configure Vercel MCP? Uses OAuth — no credentials needed."*
|
|
@@ -394,7 +394,7 @@ For each detected integration with an available MCP:
|
|
|
394
394
|
```json
|
|
395
395
|
"vercel": { "type": "http", "url": "https://mcp.vercel.com" }
|
|
396
396
|
```
|
|
397
|
-
- NO → show snippet + `
|
|
397
|
+
- NO → show snippet + `morph-spec mcp setup vercel`
|
|
398
398
|
|
|
399
399
|
Only offer Docker, Azure if explicitly detected in `.env.example`.
|
|
400
400
|
|
|
@@ -462,7 +462,7 @@ Use `AskUserQuestion` with header `"{MCP} failed"` and these options:
|
|
|
462
462
|
After all configuration is complete, run the health check to verify everything is properly installed:
|
|
463
463
|
|
|
464
464
|
```bash
|
|
465
|
-
|
|
465
|
+
morph-spec doctor
|
|
466
466
|
```
|
|
467
467
|
|
|
468
468
|
This validates: config.json integrity, agents.json presence, hooks installation, skills distribution, required command files, and state.json schema version. Fix any reported issues before proceeding.
|
|
@@ -95,7 +95,7 @@ Do not proceed while tests are failing. Fix failures and re-run before continuin
|
|
|
95
95
|
## Step 4 — Framework Validation
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
|
-
|
|
98
|
+
morph-spec validate-feature $ARGUMENTS
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
**🚫 BLOCK if it fails.**
|
|
@@ -251,10 +251,10 @@ Wait for both. **🚫 BLOCK if either returns unresolved CRITICAL.**
|
|
|
251
251
|
|
|
252
252
|
```bash
|
|
253
253
|
# Save post-review checkpoint
|
|
254
|
-
|
|
254
|
+
morph-spec checkpoint-save $ARGUMENTS --note "post-implementation review"
|
|
255
255
|
|
|
256
256
|
# Generate final recap
|
|
257
|
-
|
|
257
|
+
morph-spec generate recap $ARGUMENTS
|
|
258
258
|
```
|
|
259
259
|
|
|
260
260
|
---
|
|
@@ -152,7 +152,7 @@ All outputs go in `.morph/features/{feature-name}/5-implement/`:
|
|
|
152
152
|
|
|
153
153
|
After completion, mark the output:
|
|
154
154
|
```bash
|
|
155
|
-
|
|
155
|
+
morph-spec state mark-output {feature-name} recap
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
**Does NOT generate:** proposal.md, spec.md, contracts.cs, tasks.md
|
|
@@ -60,13 +60,13 @@ When implementing simulation as a morph-spec task:
|
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
# Track the simulation setup task
|
|
63
|
-
|
|
63
|
+
morph-spec task start {feature} {task-id}
|
|
64
64
|
|
|
65
65
|
# After implementation, validate the feature
|
|
66
|
-
|
|
66
|
+
morph-spec validate-feature {feature}
|
|
67
67
|
|
|
68
68
|
# Complete the task (triggers Tier-4 validators)
|
|
69
|
-
|
|
69
|
+
morph-spec task done {feature} {task-id}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Reference the project's service lifetime patterns:
|
|
@@ -80,7 +80,7 @@ grep -r "AddSingleton\|AddScoped\|AddTransient" src/ --include="*.cs" -l
|
|
|
80
80
|
- [ ] Validate DI: `dotnet build && dotnet run` + health check
|
|
81
81
|
- [ ] Test full flow end-to-end (not just unit tests)
|
|
82
82
|
- [ ] Verify simulation logs — confirm mocks are being called
|
|
83
|
-
- [ ] Run `
|
|
83
|
+
- [ ] Run `morph-spec validate-feature {feature}` to check architecture compliance
|
|
84
84
|
|
|
85
85
|
## Common Errors
|
|
86
86
|
|