@tekyzinc/gsd-t 2.46.11 → 2.50.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 (40) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +22 -2
  3. package/bin/debug-ledger.js +193 -0
  4. package/bin/gsd-t.js +259 -1
  5. package/commands/gsd-t-debug.md +26 -1
  6. package/commands/gsd-t-execute.md +31 -3
  7. package/commands/gsd-t-help.md +18 -2
  8. package/commands/gsd-t-integrate.md +16 -0
  9. package/commands/gsd-t-quick.md +18 -1
  10. package/commands/gsd-t-test-sync.md +5 -1
  11. package/commands/gsd-t-verify.md +6 -1
  12. package/commands/gsd-t-wave.md +26 -0
  13. package/docs/GSD-T-README.md +83 -1
  14. package/docs/architecture.md +9 -1
  15. package/docs/requirements.md +30 -0
  16. package/package.json +1 -1
  17. package/templates/CLAUDE-global.md +19 -2
  18. package/templates/stacks/_security.md +243 -0
  19. package/templates/stacks/desktop.ini +2 -0
  20. package/templates/stacks/docker.md +202 -0
  21. package/templates/stacks/firebase.md +166 -0
  22. package/templates/stacks/flutter.md +205 -0
  23. package/templates/stacks/github-actions.md +201 -0
  24. package/templates/stacks/graphql.md +216 -0
  25. package/templates/stacks/neo4j.md +218 -0
  26. package/templates/stacks/nextjs.md +184 -0
  27. package/templates/stacks/node-api.md +196 -0
  28. package/templates/stacks/playwright.md +528 -0
  29. package/templates/stacks/postgresql.md +225 -0
  30. package/templates/stacks/python.md +243 -0
  31. package/templates/stacks/react-native.md +216 -0
  32. package/templates/stacks/react.md +293 -0
  33. package/templates/stacks/redux.md +193 -0
  34. package/templates/stacks/rest-api.md +202 -0
  35. package/templates/stacks/supabase.md +188 -0
  36. package/templates/stacks/tailwind.md +169 -0
  37. package/templates/stacks/typescript.md +176 -0
  38. package/templates/stacks/vite.md +176 -0
  39. package/templates/stacks/vue.md +189 -0
  40. package/templates/stacks/zustand.md +203 -0
@@ -0,0 +1,293 @@
1
+ # React Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. Server State — React Query
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Use React Query for ALL server data fetching — NEVER useEffect + fetch
12
+ ├── NEVER store server data in useState — it belongs in the query cache
13
+ ├── useQuery for reads, useMutation for writes
14
+ └── Set staleTime explicitly — do not rely on defaults
15
+ ```
16
+
17
+ **BAD** — `useEffect(() => { fetch(...).then(setUsers); }, []);`
18
+
19
+ **GOOD**
20
+ ```tsx
21
+ const { data: users } = useQuery({
22
+ queryKey: ['users'],
23
+ queryFn: api.getUsers,
24
+ staleTime: 5 * 60 * 1000,
25
+ });
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 2. Component Design
31
+
32
+ ```
33
+ MANDATORY:
34
+ ├── Max 150 lines per component file — extract if longer
35
+ ├── Container/Presenter split: containers fetch data, presenters render UI
36
+ ├── Extract complex logic into custom hooks (useXxx)
37
+ ├── One component per file
38
+ └── No business logic in JSX — compute values above the return statement
39
+ ```
40
+
41
+ **BAD** — 300-line component mixing fetch, transform, and render logic.
42
+
43
+ **GOOD**
44
+ ```tsx
45
+ // Container fetches; Presenter renders
46
+ function UserListContainer() {
47
+ const { data } = useQuery({ queryKey: ['users'], queryFn: api.getUsers });
48
+ return <UserList users={data ?? []} />;
49
+ }
50
+ function UserList({ users }: { users: User[] }) {
51
+ return <ul>{users.map(u => <UserRow key={u.id} user={u} />)}</ul>;
52
+ }
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Props Rules
58
+
59
+ ```
60
+ MANDATORY:
61
+ ├── Define props as TypeScript interfaces
62
+ ├── Destructure props in the function signature
63
+ ├── NEVER use defaultProps — use default parameter values instead
64
+ └── Avoid prop drilling beyond 2 levels — use Context or composition
65
+ ```
66
+
67
+ **GOOD**
68
+ ```tsx
69
+ interface ButtonProps { label: string; variant?: 'primary' | 'secondary'; }
70
+ function Button({ label, variant = 'primary' }: ButtonProps) { ... }
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 4. Key Prop Rules
76
+
77
+ ```
78
+ MANDATORY:
79
+ ├── NEVER use array index as key on dynamic (add/remove/reorder) lists
80
+ ├── Use stable unique IDs from data (item.id, item.slug)
81
+ └── Never generate keys at render time (Math.random(), Date.now())
82
+ ```
83
+
84
+ **BAD** — `items.map((item, i) => <Row key={i} />)`
85
+
86
+ **GOOD** — `items.map(item => <Row key={item.id} />)`
87
+
88
+ ---
89
+
90
+ ## 5. Hooks Rules
91
+
92
+ ```
93
+ MANDATORY:
94
+ ├── Only call hooks at the top level — NEVER inside conditionals or loops
95
+ ├── NEVER call a hook conditionally — restructure using early JSX returns
96
+ ├── Custom hooks MUST start with "use" prefix
97
+ └── One concern per custom hook
98
+ ```
99
+
100
+ **BAD** — `if (isLoggedIn) { const data = useUserData(); }`
101
+
102
+ **GOOD** — Call `useUserData()` unconditionally; return early in JSX if not logged in.
103
+
104
+ ---
105
+
106
+ ## 6. Memoization
107
+
108
+ ```
109
+ USE SPARINGLY — profile first, optimize second:
110
+ ├── React.memo: only when parent re-renders often AND child props rarely change
111
+ ├── useMemo: only for computationally expensive operations (not string/array literals)
112
+ └── useCallback: only when the function is passed to a memoized child or is a
113
+ useEffect dependency
114
+ ```
115
+
116
+ **BAD** — `const label = useMemo(() => \`Hello, ${name}\`, [name]);` (trivial — no benefit)
117
+
118
+ **GOOD** — `const sorted = useMemo(() => items.sort(expensiveSort), [items]);`
119
+
120
+ ---
121
+
122
+ ## 7. Lazy Loading
123
+
124
+ ```
125
+ MANDATORY for route-level components:
126
+ ├── Wrap with React.lazy() + Suspense
127
+ ├── Always provide a Suspense fallback (skeleton or spinner — not null)
128
+ └── Group related routes in one lazy chunk to minimize round trips
129
+ ```
130
+
131
+ ```tsx
132
+ const Dashboard = React.lazy(() => import('./Dashboard'));
133
+ <Suspense fallback={<DashboardSkeleton />}><Dashboard /></Suspense>
134
+ ```
135
+
136
+ ---
137
+
138
+ ## 8. Error Boundaries
139
+
140
+ ```
141
+ MANDATORY:
142
+ ├── Wrap every route and major feature section in an ErrorBoundary
143
+ ├── Display a user-friendly fallback UI — never a blank screen
144
+ ├── Log to error tracking (Sentry) in componentDidCatch
145
+ └── Use granular boundaries — one top-level boundary is not enough
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 9. Accessibility (a11y)
151
+
152
+ ```
153
+ MANDATORY:
154
+ ├── Use semantic HTML (button, nav, main, header, section)
155
+ ├── All interactive elements MUST be keyboard-navigable (Tab, Enter, Escape)
156
+ ├── Images require alt text (alt="" for decorative)
157
+ ├── Form inputs MUST have associated <label> (htmlFor + id)
158
+ ├── Modals: trap focus inside, restore on close, Escape closes
159
+ ├── Icon-only buttons require aria-label
160
+ └── NEVER remove focus outlines — style them, never hide them
161
+ ```
162
+
163
+ **BAD** — `<div onClick={del}>Delete</div>` / `<img src={x} />`
164
+
165
+ **GOOD** — `<button onClick={del} aria-label="Delete">Delete</button>` / `<img src={x} alt="Profile photo" />`
166
+
167
+ ---
168
+
169
+ ## 10. State Management — When to Use What
170
+
171
+ ```
172
+ MANDATORY — choose the right tool for the data type:
173
+ ├── UI toggles, form inputs → useState
174
+ ├── Complex local state (multi-step) → useReducer
175
+ ├── Server/API data → React Query (NEVER useState)
176
+ ├── Auth session, permissions → React Context
177
+ ├── Theme preference → React Context + localStorage
178
+ └── URL state (page, filters) → React Router (useSearchParams)
179
+ ```
180
+
181
+ **Rules:**
182
+ - Never duplicate server state in local state — React Query is the single source of truth for API data
183
+ - Lift state only when needed — if two siblings need the same state, lift to parent, not a global store
184
+ - Derive, don't store — if a value can be computed from existing state, compute it
185
+
186
+ **BAD** — storing derived state:
187
+ ```tsx
188
+ const [users, setUsers] = useState(allUsers);
189
+ const [filteredUsers, setFilteredUsers] = useState([]);
190
+ const [userCount, setUserCount] = useState(0);
191
+ ```
192
+
193
+ **GOOD** — derive from source:
194
+ ```tsx
195
+ const [filter, setFilter] = useState('all');
196
+ const filteredUsers = useMemo(
197
+ () => users.filter(u => filter === 'all' || u.status === filter),
198
+ [users, filter]
199
+ );
200
+ const userCount = filteredUsers.length;
201
+ ```
202
+
203
+ ---
204
+
205
+ ## 11. Form Management — react-hook-form + Zod
206
+
207
+ ```
208
+ MANDATORY when project uses forms:
209
+ ├── One Zod schema per form — schema is the single source of truth for validation
210
+ ├── Use react-hook-form with zodResolver — NEVER validate in event handlers
211
+ ├── Show field-level errors below the field — not in a toast
212
+ ├── Disable submit button while submitting
213
+ ├── Use noValidate on <form> — browser validation conflicts with custom validation
214
+ └── Use setError('root', ...) for server-side errors
215
+ ```
216
+
217
+ **GOOD**
218
+ ```tsx
219
+ const schema = z.object({
220
+ email: z.string().email('Enter a valid email'),
221
+ name: z.string().min(2, 'Name must be at least 2 characters'),
222
+ });
223
+ type FormData = z.infer<typeof schema>;
224
+
225
+ function MyForm() {
226
+ const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
227
+ resolver: zodResolver(schema),
228
+ });
229
+ return (
230
+ <form onSubmit={handleSubmit(onSubmit)} noValidate>
231
+ <label htmlFor="email">Email</label>
232
+ <input id="email" {...register('email')} />
233
+ {errors.email && <span className="error">{errors.email.message}</span>}
234
+ <button type="submit" disabled={isSubmitting}>Save</button>
235
+ </form>
236
+ );
237
+ }
238
+ ```
239
+
240
+ ---
241
+
242
+ ## 12. React Naming Conventions
243
+
244
+ ```
245
+ MANDATORY:
246
+ ├── Components: PascalCase (UserList.tsx, DataTable.tsx)
247
+ ├── Hooks: camelCase + use (useAuth.ts, useUsers.ts)
248
+ ├── Services: camelCase + Service (userService.ts)
249
+ ├── Event handlers: handle prefix (handleClick, handleSubmit)
250
+ ├── Callback props: on prefix (onClick, onClose, onChange)
251
+ ├── Boolean state: is/has/can prefix (isOpen, hasError, canEdit)
252
+ ├── Folders: kebab-case (user-settings/, danger-window/)
253
+ └── Non-component files: camelCase (helpers.ts, permissions.ts)
254
+ ```
255
+
256
+ ---
257
+
258
+ ## 13. Anti-Patterns
259
+
260
+ ```
261
+ NEVER:
262
+ ├── useEffect for data fetching (use React Query — Section 1)
263
+ ├── Prop drilling beyond 2 levels
264
+ ├── Array index as key on dynamic lists (Section 4)
265
+ ├── Conditional hook calls (Section 5)
266
+ ├── Direct state mutation: state.list.push(x) — return new objects/arrays
267
+ ├── console.log in committed code
268
+ ├── Derived state in useState when it can be computed (Section 10)
269
+ ├── Validate forms in event handlers (use react-hook-form + zod — Section 11)
270
+ └── dangerouslySetInnerHTML without sanitization — see _security.md
271
+ ```
272
+
273
+ ---
274
+
275
+ ## React Verification Checklist
276
+
277
+ - [ ] Server data fetched with React Query — no `useEffect` + fetch
278
+ - [ ] No component exceeds 150 lines
279
+ - [ ] Container/Presenter pattern applied to data-fetching components
280
+ - [ ] All props typed with TypeScript interfaces
281
+ - [ ] No array index as key on dynamic lists
282
+ - [ ] No conditional hook calls
283
+ - [ ] Memoization justified — not preemptive
284
+ - [ ] Route components use `React.lazy` + `Suspense` with a fallback
285
+ - [ ] `ErrorBoundary` wraps each major feature section
286
+ - [ ] All interactive elements keyboard-accessible with ARIA labels
287
+ - [ ] No `console.log` in committed code
288
+ - [ ] No direct state mutations — always return new objects/arrays
289
+ - [ ] `dangerouslySetInnerHTML` usage reviewed against `_security.md`
290
+ - [ ] State tool matches data type (useState/useReducer/Context/React Query/Router)
291
+ - [ ] No derived state in useState — compute from source
292
+ - [ ] Forms use react-hook-form + zod — no manual validation in handlers
293
+ - [ ] React naming conventions followed (handle*, on*, is/has/can)
@@ -0,0 +1,193 @@
1
+ # Redux Toolkit Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. RTK Only — No Legacy Redux
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Use @reduxjs/toolkit (RTK) — NEVER raw redux with manual action creators/reducers
12
+ ├── Use createSlice for all state slices
13
+ ├── Use configureStore — NEVER createStore
14
+ ├── Use RTK Query for server data — NEVER manual async thunks for API calls
15
+ └── Use TypeScript with typed hooks (useAppSelector, useAppDispatch)
16
+ ```
17
+
18
+ ---
19
+
20
+ ## 2. Slice Design
21
+
22
+ ```
23
+ MANDATORY:
24
+ ├── One slice per domain: userSlice, cartSlice, uiSlice
25
+ ├── Name slices clearly: name: 'auth', name: 'cart'
26
+ ├── Define state interface with TypeScript
27
+ ├── Reducers handle one concern — keep them focused
28
+ ├── Use prepare callbacks for action payload shaping
29
+ └── Export actions and reducer separately
30
+ ```
31
+
32
+ **GOOD**
33
+ ```typescript
34
+ interface AuthState {
35
+ user: User | null;
36
+ status: 'idle' | 'loading' | 'succeeded' | 'failed';
37
+ error: string | null;
38
+ }
39
+
40
+ const initialState: AuthState = {
41
+ user: null,
42
+ status: 'idle',
43
+ error: null,
44
+ };
45
+
46
+ const authSlice = createSlice({
47
+ name: 'auth',
48
+ initialState,
49
+ reducers: {
50
+ setUser: (state, action: PayloadAction<User>) => {
51
+ state.user = action.payload;
52
+ state.status = 'succeeded';
53
+ },
54
+ logout: (state) => {
55
+ state.user = null;
56
+ state.status = 'idle';
57
+ },
58
+ },
59
+ });
60
+
61
+ export const { setUser, logout } = authSlice.actions;
62
+ export default authSlice.reducer;
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 3. RTK Query — Server Data
68
+
69
+ ```
70
+ MANDATORY:
71
+ ├── Use RTK Query for ALL API calls — NEVER createAsyncThunk for data fetching
72
+ ├── Define one API slice per backend service
73
+ ├── Use tag-based cache invalidation — not manual cache updates
74
+ ├── Type all endpoints with request/response types
75
+ ├── Handle loading, error, and empty states using hook results
76
+ └── Set keepUnusedDataFor to control cache lifetime
77
+ ```
78
+
79
+ **GOOD**
80
+ ```typescript
81
+ export const usersApi = createApi({
82
+ reducerPath: 'usersApi',
83
+ baseQuery: fetchBaseQuery({ baseUrl: '/api/v1' }),
84
+ tagTypes: ['User'],
85
+ endpoints: (builder) => ({
86
+ getUsers: builder.query<User[], UserFilters>({
87
+ query: (filters) => ({ url: '/users', params: filters }),
88
+ providesTags: ['User'],
89
+ }),
90
+ getUser: builder.query<User, string>({
91
+ query: (id) => `/users/${id}`,
92
+ providesTags: (result, error, id) => [{ type: 'User', id }],
93
+ }),
94
+ createUser: builder.mutation<User, CreateUserInput>({
95
+ query: (body) => ({ url: '/users', method: 'POST', body }),
96
+ invalidatesTags: ['User'],
97
+ }),
98
+ }),
99
+ });
100
+
101
+ export const { useGetUsersQuery, useGetUserQuery, useCreateUserMutation } = usersApi;
102
+ ```
103
+
104
+ ---
105
+
106
+ ## 4. Store Configuration
107
+
108
+ ```
109
+ MANDATORY:
110
+ ├── configureStore with typed RootState and AppDispatch
111
+ ├── Add RTK Query middleware for cache management
112
+ ├── Create typed hooks: useAppSelector, useAppDispatch
113
+ ├── Redux DevTools enabled by default in development
114
+ └── Keep store configuration in a single store.ts file
115
+ ```
116
+
117
+ **GOOD**
118
+ ```typescript
119
+ // store.ts
120
+ export const store = configureStore({
121
+ reducer: {
122
+ auth: authReducer,
123
+ cart: cartReducer,
124
+ [usersApi.reducerPath]: usersApi.reducer,
125
+ },
126
+ middleware: (getDefaultMiddleware) =>
127
+ getDefaultMiddleware().concat(usersApi.middleware),
128
+ });
129
+
130
+ export type RootState = ReturnType<typeof store.getState>;
131
+ export type AppDispatch = typeof store.dispatch;
132
+
133
+ // hooks.ts
134
+ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
135
+ export const useAppDispatch: () => AppDispatch = useDispatch;
136
+ ```
137
+
138
+ ---
139
+
140
+ ## 5. Selectors
141
+
142
+ ```
143
+ MANDATORY:
144
+ ├── Use createSelector (reselect) for derived/computed state
145
+ ├── Select minimal state — NEVER select the entire slice
146
+ ├── Co-locate selectors with their slice file
147
+ ├── Name selectors: select{Thing} (selectActiveUsers, selectCartTotal)
148
+ └── Memoized selectors for filtered/sorted/computed data
149
+ ```
150
+
151
+ **GOOD**
152
+ ```typescript
153
+ // In authSlice.ts
154
+ export const selectUser = (state: RootState) => state.auth.user;
155
+ export const selectIsAuthenticated = (state: RootState) => !!state.auth.user;
156
+
157
+ // Memoized computed selector
158
+ export const selectActiveUsers = createSelector(
159
+ [(state: RootState) => state.users.list],
160
+ (users) => users.filter(u => u.isActive)
161
+ );
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 6. Anti-Patterns
167
+
168
+ ```
169
+ NEVER:
170
+ ├── Raw redux (createStore, manual action types, switch reducers)
171
+ ├── createAsyncThunk for API calls — use RTK Query
172
+ ├── Selecting entire slice in a component
173
+ ├── Storing server data in slices — use RTK Query
174
+ ├── Mutating state outside of createSlice reducers (Immer only works inside)
175
+ ├── String action types — use createSlice auto-generated types
176
+ ├── Dispatching in useEffect for data fetching — use RTK Query hooks
177
+ └── console.log in reducers
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Redux Toolkit Verification Checklist
183
+
184
+ - [ ] RTK only — no legacy redux patterns
185
+ - [ ] createSlice for all state management
186
+ - [ ] RTK Query for all API calls — no createAsyncThunk for fetching
187
+ - [ ] Tag-based cache invalidation on mutations
188
+ - [ ] Typed store (RootState, AppDispatch, typed hooks)
189
+ - [ ] Selectors with createSelector for computed state
190
+ - [ ] Minimal state selected — no full-slice subscriptions
191
+ - [ ] Store configured with RTK Query middleware
192
+ - [ ] Loading, error, empty states handled via hook results
193
+ - [ ] No console.log in reducers or slices
@@ -0,0 +1,202 @@
1
+ # REST API Design Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. URL Design
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Nouns, not verbs: /users, /orders — NEVER /getUsers, /createOrder
12
+ ├── Plural collection names: /users, /products, /orders
13
+ ├── Nested resources for relationships: /users/{id}/orders
14
+ ├── Max 2 levels of nesting — flatten beyond that
15
+ ├── Kebab-case for multi-word paths: /order-items — not /orderItems or /order_items
16
+ ├── API version in URL path: /api/v1/users — not in headers
17
+ └── NEVER expose internal IDs or database structure in URLs
18
+ ```
19
+
20
+ **BAD** — `/api/getUserById?id=123`, `/api/v1/order_items`
21
+
22
+ **GOOD** — `/api/v1/users/123`, `/api/v1/order-items`
23
+
24
+ ---
25
+
26
+ ## 2. HTTP Methods
27
+
28
+ ```
29
+ MANDATORY:
30
+ ├── GET: Read (no side effects, cacheable)
31
+ ├── POST: Create new resource (returns 201 + Location header)
32
+ ├── PUT: Full replace of a resource
33
+ ├── PATCH: Partial update of a resource
34
+ ├── DELETE: Remove a resource (returns 204 or 200)
35
+ ├── GET must NEVER modify data
36
+ └── POST is not a catch-all — use the correct method
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 3. Response Format
42
+
43
+ ```
44
+ MANDATORY — consistent envelope for all responses:
45
+ ├── Success: { "data": {...} } or { "data": [...] }
46
+ ├── Collection: { "data": [...], "meta": { "total": N, "page": 1, "pageSize": 20 } }
47
+ ├── Error: { "error": { "code": "NOT_FOUND", "message": "User not found" } }
48
+ ├── Use camelCase for JSON keys — matches JavaScript conventions
49
+ ├── Include timestamps as ISO 8601 strings with timezone (2026-03-25T10:00:00Z)
50
+ └── Null fields: include with null value — don't omit the key
51
+ ```
52
+
53
+ **GOOD**
54
+ ```json
55
+ {
56
+ "data": {
57
+ "id": "abc-123",
58
+ "email": "user@example.com",
59
+ "displayName": "Jane Doe",
60
+ "createdAt": "2026-03-25T10:00:00Z",
61
+ "avatar": null
62
+ }
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 4. Pagination
69
+
70
+ ```
71
+ MANDATORY for all list endpoints:
72
+ ├── Offset-based: ?page=1&pageSize=20 (simple, good for UI pages)
73
+ ├── Cursor-based: ?cursor=abc&limit=20 (better for large/realtime datasets)
74
+ ├── Default pageSize: 20, max pageSize: 100
75
+ ├── Include pagination metadata in response: total, page, pageSize, hasMore
76
+ ├── NEVER return unbounded lists — always paginate or limit
77
+ └── Support sorting: ?sort=createdAt&order=desc
78
+ ```
79
+
80
+ **GOOD**
81
+ ```json
82
+ {
83
+ "data": [...],
84
+ "meta": {
85
+ "total": 342,
86
+ "page": 2,
87
+ "pageSize": 20,
88
+ "hasMore": true
89
+ }
90
+ }
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 5. Error Responses
96
+
97
+ ```
98
+ MANDATORY:
99
+ ├── Use standard HTTP status codes correctly (see table below)
100
+ ├── Error body includes: code (machine-readable), message (human-readable)
101
+ ├── Validation errors include field-level details
102
+ ├── NEVER expose stack traces, SQL errors, or internal paths to clients
103
+ └── Log the full error server-side — return a safe summary to the client
104
+ ```
105
+
106
+ | Status | Meaning | When to use |
107
+ |--------|---------|------------|
108
+ | 200 | OK | Successful GET, PUT, PATCH, DELETE |
109
+ | 201 | Created | Successful POST (include Location header) |
110
+ | 204 | No Content | Successful DELETE with no response body |
111
+ | 400 | Bad Request | Invalid input, malformed JSON, validation error |
112
+ | 401 | Unauthorized | Missing or invalid authentication |
113
+ | 403 | Forbidden | Authenticated but insufficient permissions |
114
+ | 404 | Not Found | Resource doesn't exist |
115
+ | 409 | Conflict | Duplicate resource, optimistic lock failure |
116
+ | 422 | Unprocessable Entity | Valid JSON but semantic errors |
117
+ | 429 | Too Many Requests | Rate limit exceeded |
118
+ | 500 | Internal Server Error | Unexpected server failure |
119
+
120
+ **Validation error example:**
121
+ ```json
122
+ {
123
+ "error": {
124
+ "code": "VALIDATION_ERROR",
125
+ "message": "Request validation failed",
126
+ "details": [
127
+ { "field": "email", "message": "Must be a valid email address" },
128
+ { "field": "name", "message": "Must be at least 2 characters" }
129
+ ]
130
+ }
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 6. Filtering and Search
137
+
138
+ ```
139
+ MANDATORY:
140
+ ├── Filter via query params: ?status=active&role=admin
141
+ ├── Search via query param: ?search=jane (server decides which fields to search)
142
+ ├── Date ranges: ?createdAfter=2026-01-01&createdBefore=2026-03-01
143
+ ├── Multiple values: ?status=active,pending (comma-separated)
144
+ ├── NEVER accept raw SQL or query expressions from the client
145
+ └── Validate and whitelist all filter parameters
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 7. Versioning
151
+
152
+ ```
153
+ MANDATORY:
154
+ ├── Version in URL path: /api/v1/, /api/v2/
155
+ ├── Bump major version only for breaking changes
156
+ ├── Support previous version for a deprecation period (minimum 3 months)
157
+ ├── Document breaking changes in a changelog
158
+ └── New fields are NOT breaking changes — clients should ignore unknown fields
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 8. Rate Limiting
164
+
165
+ ```
166
+ MANDATORY for public APIs:
167
+ ├── Implement rate limiting per client/API key
168
+ ├── Return 429 with Retry-After header
169
+ ├── Include rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
170
+ └── Different limits per tier (free vs paid) if applicable
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 9. Anti-Patterns
176
+
177
+ ```
178
+ NEVER:
179
+ ├── Verbs in URLs (/getUser, /deleteOrder)
180
+ ├── GET requests that modify data
181
+ ├── Exposing internal errors, stack traces, or SQL to clients
182
+ ├── Unbounded list responses without pagination
183
+ ├── Inconsistent response shapes between endpoints
184
+ ├── Accepting raw query expressions from clients
185
+ ├── Breaking changes without version bump
186
+ └── 200 OK with error body — use proper status codes
187
+ ```
188
+
189
+ ---
190
+
191
+ ## REST API Verification Checklist
192
+
193
+ - [ ] URLs use nouns, plural, kebab-case
194
+ - [ ] Correct HTTP methods (GET reads, POST creates, etc.)
195
+ - [ ] Consistent response envelope (data, error, meta)
196
+ - [ ] All list endpoints paginated with metadata
197
+ - [ ] Error responses include code + message, no internals exposed
198
+ - [ ] Validation errors include field-level details
199
+ - [ ] API versioned in URL path
200
+ - [ ] Rate limiting with proper headers (if public)
201
+ - [ ] Timestamps in ISO 8601 with timezone
202
+ - [ ] Filtering via whitelisted query params only