@torus-engineering/tas-kit 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.claude/agents/README.md +83 -0
  2. package/.claude/agents/architect.md +53 -0
  3. package/.claude/agents/aws-reviewer.md +71 -0
  4. package/.claude/agents/build-resolver.md +59 -0
  5. package/.claude/agents/code-architect.md +62 -0
  6. package/.claude/agents/code-explorer.md +63 -0
  7. package/.claude/agents/code-simplifier.md +53 -0
  8. package/.claude/agents/comment-analyzer.md +59 -0
  9. package/.claude/agents/conversation-analyzer.md +57 -0
  10. package/.claude/agents/csharp-reviewer.md +62 -0
  11. package/.claude/agents/database-reviewer.md +73 -0
  12. package/.claude/agents/doc-updater.md +66 -0
  13. package/.claude/agents/docs-lookup.md +55 -0
  14. package/.claude/agents/e2e-runner.md +61 -0
  15. package/.claude/agents/harness-optimizer.md +62 -0
  16. package/.claude/agents/loop-operator.md +56 -0
  17. package/.claude/agents/performance-optimizer.md +78 -0
  18. package/.claude/agents/planner.md +82 -0
  19. package/.claude/agents/pr-test-analyzer.md +68 -0
  20. package/.claude/agents/python-reviewer.md +67 -0
  21. package/.claude/agents/pytorch-build-resolver.md +76 -0
  22. package/.claude/agents/refactor-cleaner.md +70 -0
  23. package/.claude/agents/security-reviewer.md +79 -0
  24. package/.claude/agents/seo-specialist.md +75 -0
  25. package/.claude/agents/silent-failure-hunter.md +69 -0
  26. package/.claude/agents/tdd-guide.md +84 -0
  27. package/.claude/agents/type-design-analyzer.md +75 -0
  28. package/.claude/agents/typescript-reviewer.md +65 -0
  29. package/.claude/commands/ado-create.md +2 -1
  30. package/.claude/commands/ado-delete.md +3 -2
  31. package/.claude/commands/ado-get.md +2 -1
  32. package/.claude/commands/ado-status.md +2 -1
  33. package/.claude/commands/ado-update.md +2 -1
  34. package/.claude/commands/tas-adr.md +13 -12
  35. package/.claude/commands/tas-bug.md +97 -50
  36. package/.claude/commands/tas-design.md +3 -1
  37. package/.claude/commands/tas-dev.md +115 -0
  38. package/.claude/commands/tas-epic.md +4 -2
  39. package/.claude/commands/tas-feature.md +5 -3
  40. package/.claude/commands/tas-fix.md +47 -0
  41. package/.claude/commands/tas-plan.md +184 -0
  42. package/.claude/commands/tas-prd.md +3 -1
  43. package/.claude/commands/tas-review.md +104 -0
  44. package/.claude/commands/tas-sad.md +3 -1
  45. package/.claude/commands/tas-security.md +80 -0
  46. package/.claude/commands/tas-spec.md +50 -0
  47. package/.claude/commands/tas-story.md +77 -40
  48. package/.claude/commands/tas-verify.md +8 -0
  49. package/.claude/hooks/code-quality.js +127 -0
  50. package/.claude/hooks/session-end.js +116 -0
  51. package/.claude/rules/.gitkeep +0 -0
  52. package/.claude/rules/common/agents.md +65 -0
  53. package/.claude/rules/common/code-review.md +124 -0
  54. package/.claude/rules/common/coding-style.md +90 -0
  55. package/.claude/rules/common/development-workflow.md +44 -0
  56. package/.claude/rules/common/git-workflow.md +24 -0
  57. package/.claude/rules/common/hooks.md +30 -0
  58. package/.claude/rules/common/patterns.md +31 -0
  59. package/.claude/rules/common/performance.md +55 -0
  60. package/.claude/rules/common/post-review-agent.md +39 -0
  61. package/.claude/rules/common/project-status.md +80 -0
  62. package/.claude/rules/common/security.md +29 -0
  63. package/.claude/rules/common/stack-detection.md +29 -0
  64. package/.claude/rules/common/testing.md +57 -0
  65. package/.claude/rules/csharp/coding-style.md +72 -0
  66. package/.claude/rules/csharp/hooks.md +25 -0
  67. package/.claude/rules/csharp/patterns.md +50 -0
  68. package/.claude/rules/csharp/security.md +58 -0
  69. package/.claude/rules/csharp/testing.md +46 -0
  70. package/.claude/rules/python/coding-style.md +42 -0
  71. package/.claude/rules/python/hooks.md +19 -0
  72. package/.claude/rules/python/patterns.md +39 -0
  73. package/.claude/rules/python/security.md +30 -0
  74. package/.claude/rules/python/testing.md +38 -0
  75. package/.claude/rules/typescript/coding-style.md +199 -0
  76. package/.claude/rules/typescript/hooks.md +22 -0
  77. package/.claude/rules/typescript/patterns.md +52 -0
  78. package/.claude/rules/typescript/security.md +28 -0
  79. package/.claude/rules/typescript/testing.md +18 -0
  80. package/.claude/rules/web/coding-style.md +96 -0
  81. package/.claude/rules/web/design-quality.md +63 -0
  82. package/.claude/rules/web/hooks.md +120 -0
  83. package/.claude/rules/web/patterns.md +79 -0
  84. package/.claude/rules/web/performance.md +64 -0
  85. package/.claude/rules/web/security.md +57 -0
  86. package/.claude/rules/web/testing.md +55 -0
  87. package/.claude/settings.json +37 -0
  88. package/.claude/settings.local.json +38 -0
  89. package/.claude/skills/ado-integration/SKILL.md +44 -1
  90. package/.claude/skills/agent-harness-construction/SKILL.md +77 -0
  91. package/.claude/skills/agent-introspection-debugging/SKILL.md +157 -0
  92. package/.claude/skills/ai-regression-testing/SKILL.md +364 -0
  93. package/.claude/skills/api-design/SKILL.md +528 -0
  94. package/.claude/skills/architecture-decision-records/SKILL.md +184 -0
  95. package/.claude/skills/backend-patterns/SKILL.md +602 -0
  96. package/.claude/skills/benchmark/SKILL.md +98 -0
  97. package/.claude/skills/browser-qa/SKILL.md +92 -0
  98. package/.claude/skills/canary-watch/SKILL.md +104 -0
  99. package/.claude/skills/tas-conventions/SKILL.md +51 -3
  100. package/.claude/skills/tas-implementation-complete/SKILL.md +97 -0
  101. package/.claude/skills/tas-tdd/SKILL.md +72 -16
  102. package/.tas/README.md +29 -24
  103. package/.tas/tas-example.yaml +2 -1
  104. package/.tas/templates/Story.md +18 -18
  105. package/CLAUDE-Example.md +1 -1
  106. package/README.md +20 -5
  107. package/package.json +1 -1
  108. package/.claude/commands/tas-dev-story.md +0 -61
  109. package/.claude/commands/tas-review-code.md +0 -42
  110. package/.claude/commands/tas-security-check.md +0 -30
@@ -0,0 +1,602 @@
1
+ ---
2
+ name: backend-patterns
3
+ description: |
4
+ Auto-invoke when implementing repository, service, or controller layers on Node.js/Express/Next.js,
5
+ fixing N+1 query problems, setting up Redis caching, adding auth middleware, or structuring
6
+ background job processing. NOT for API contract design (use api-design instead).
7
+ origin: ECC
8
+ allowed-tools: Read, Grep, Glob
9
+ ---
10
+
11
+ # Backend Development Patterns
12
+
13
+ Backend architecture patterns and best practices for scalable server-side applications.
14
+
15
+ ## When to Activate
16
+
17
+ - Designing REST or GraphQL API endpoints
18
+ - Implementing repository, service, or controller layers
19
+ - Optimizing database queries (N+1, indexing, connection pooling)
20
+ - Adding caching (Redis, in-memory, HTTP cache headers)
21
+ - Setting up background jobs or async processing
22
+ - Structuring error handling and validation for APIs
23
+ - Building middleware (auth, logging, rate limiting)
24
+
25
+ ## API Design Patterns
26
+
27
+ ### RESTful API Structure
28
+
29
+ ```typescript
30
+ // PASS: Resource-based URLs
31
+ GET /api/markets # List resources
32
+ GET /api/markets/:id # Get single resource
33
+ POST /api/markets # Create resource
34
+ PUT /api/markets/:id # Replace resource
35
+ PATCH /api/markets/:id # Update resource
36
+ DELETE /api/markets/:id # Delete resource
37
+
38
+ // PASS: Query parameters for filtering, sorting, pagination
39
+ GET /api/markets?status=active&sort=volume&limit=20&offset=0
40
+ ```
41
+
42
+ ### Repository Pattern
43
+
44
+ ```typescript
45
+ // Abstract data access logic
46
+ interface MarketRepository {
47
+ findAll(filters?: MarketFilters): Promise<Market[]>
48
+ findById(id: string): Promise<Market | null>
49
+ create(data: CreateMarketDto): Promise<Market>
50
+ update(id: string, data: UpdateMarketDto): Promise<Market>
51
+ delete(id: string): Promise<void>
52
+ }
53
+
54
+ class SupabaseMarketRepository implements MarketRepository {
55
+ async findAll(filters?: MarketFilters): Promise<Market[]> {
56
+ let query = supabase.from('markets').select('*')
57
+
58
+ if (filters?.status) {
59
+ query = query.eq('status', filters.status)
60
+ }
61
+
62
+ if (filters?.limit) {
63
+ query = query.limit(filters.limit)
64
+ }
65
+
66
+ const { data, error } = await query
67
+
68
+ if (error) throw new Error(error.message)
69
+ return data
70
+ }
71
+
72
+ // Other methods...
73
+ }
74
+ ```
75
+
76
+ ### Service Layer Pattern
77
+
78
+ ```typescript
79
+ // Business logic separated from data access
80
+ class MarketService {
81
+ constructor(private marketRepo: MarketRepository) {}
82
+
83
+ async searchMarkets(query: string, limit: number = 10): Promise<Market[]> {
84
+ // Business logic
85
+ const embedding = await generateEmbedding(query)
86
+ const results = await this.vectorSearch(embedding, limit)
87
+
88
+ // Fetch full data
89
+ const markets = await this.marketRepo.findByIds(results.map(r => r.id))
90
+
91
+ // Sort by similarity
92
+ return markets.sort((a, b) => {
93
+ const scoreA = results.find(r => r.id === a.id)?.score || 0
94
+ const scoreB = results.find(r => r.id === b.id)?.score || 0
95
+ return scoreA - scoreB
96
+ })
97
+ }
98
+
99
+ private async vectorSearch(embedding: number[], limit: number) {
100
+ // Vector search implementation
101
+ }
102
+ }
103
+ ```
104
+
105
+ ### Middleware Pattern
106
+
107
+ ```typescript
108
+ // Request/response processing pipeline
109
+ export function withAuth(handler: NextApiHandler): NextApiHandler {
110
+ return async (req, res) => {
111
+ const token = req.headers.authorization?.replace('Bearer ', '')
112
+
113
+ if (!token) {
114
+ return res.status(401).json({ error: 'Unauthorized' })
115
+ }
116
+
117
+ try {
118
+ const user = await verifyToken(token)
119
+ req.user = user
120
+ return handler(req, res)
121
+ } catch (error) {
122
+ return res.status(401).json({ error: 'Invalid token' })
123
+ }
124
+ }
125
+ }
126
+
127
+ // Usage
128
+ export default withAuth(async (req, res) => {
129
+ // Handler has access to req.user
130
+ })
131
+ ```
132
+
133
+ ## Database Patterns
134
+
135
+ ### Query Optimization
136
+
137
+ ```typescript
138
+ // PASS: GOOD: Select only needed columns
139
+ const { data } = await supabase
140
+ .from('markets')
141
+ .select('id, name, status, volume')
142
+ .eq('status', 'active')
143
+ .order('volume', { ascending: false })
144
+ .limit(10)
145
+
146
+ // FAIL: BAD: Select everything
147
+ const { data } = await supabase
148
+ .from('markets')
149
+ .select('*')
150
+ ```
151
+
152
+ ### N+1 Query Prevention
153
+
154
+ ```typescript
155
+ // FAIL: BAD: N+1 query problem
156
+ const markets = await getMarkets()
157
+ for (const market of markets) {
158
+ market.creator = await getUser(market.creator_id) // N queries
159
+ }
160
+
161
+ // PASS: GOOD: Batch fetch
162
+ const markets = await getMarkets()
163
+ const creatorIds = markets.map(m => m.creator_id)
164
+ const creators = await getUsers(creatorIds) // 1 query
165
+ const creatorMap = new Map(creators.map(c => [c.id, c]))
166
+
167
+ markets.forEach(market => {
168
+ market.creator = creatorMap.get(market.creator_id)
169
+ })
170
+ ```
171
+
172
+ ### Transaction Pattern
173
+
174
+ ```typescript
175
+ async function createMarketWithPosition(
176
+ marketData: CreateMarketDto,
177
+ positionData: CreatePositionDto
178
+ ) {
179
+ // Use Supabase transaction
180
+ const { data, error } = await supabase.rpc('create_market_with_position', {
181
+ market_data: marketData,
182
+ position_data: positionData
183
+ })
184
+
185
+ if (error) throw new Error('Transaction failed')
186
+ return data
187
+ }
188
+
189
+ // SQL function in Supabase
190
+ CREATE OR REPLACE FUNCTION create_market_with_position(
191
+ market_data jsonb,
192
+ position_data jsonb
193
+ )
194
+ RETURNS jsonb
195
+ LANGUAGE plpgsql
196
+ AS $$
197
+ BEGIN
198
+ -- Start transaction automatically
199
+ INSERT INTO markets VALUES (market_data);
200
+ INSERT INTO positions VALUES (position_data);
201
+ RETURN jsonb_build_object('success', true);
202
+ EXCEPTION
203
+ WHEN OTHERS THEN
204
+ -- Rollback happens automatically
205
+ RETURN jsonb_build_object('success', false, 'error', SQLERRM);
206
+ END;
207
+ $$;
208
+ ```
209
+
210
+ ## Caching Strategies
211
+
212
+ ### Redis Caching Layer
213
+
214
+ ```typescript
215
+ class CachedMarketRepository implements MarketRepository {
216
+ constructor(
217
+ private baseRepo: MarketRepository,
218
+ private redis: RedisClient
219
+ ) {}
220
+
221
+ async findById(id: string): Promise<Market | null> {
222
+ // Check cache first
223
+ const cached = await this.redis.get(`market:${id}`)
224
+
225
+ if (cached) {
226
+ return JSON.parse(cached)
227
+ }
228
+
229
+ // Cache miss - fetch from database
230
+ const market = await this.baseRepo.findById(id)
231
+
232
+ if (market) {
233
+ // Cache for 5 minutes
234
+ await this.redis.setex(`market:${id}`, 300, JSON.stringify(market))
235
+ }
236
+
237
+ return market
238
+ }
239
+
240
+ async invalidateCache(id: string): Promise<void> {
241
+ await this.redis.del(`market:${id}`)
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### Cache-Aside Pattern
247
+
248
+ ```typescript
249
+ async function getMarketWithCache(id: string): Promise<Market> {
250
+ const cacheKey = `market:${id}`
251
+
252
+ // Try cache
253
+ const cached = await redis.get(cacheKey)
254
+ if (cached) return JSON.parse(cached)
255
+
256
+ // Cache miss - fetch from DB
257
+ const market = await db.markets.findUnique({ where: { id } })
258
+
259
+ if (!market) throw new Error('Market not found')
260
+
261
+ // Update cache
262
+ await redis.setex(cacheKey, 300, JSON.stringify(market))
263
+
264
+ return market
265
+ }
266
+ ```
267
+
268
+ ## Error Handling Patterns
269
+
270
+ ### Centralized Error Handler
271
+
272
+ ```typescript
273
+ class ApiError extends Error {
274
+ constructor(
275
+ public statusCode: number,
276
+ public message: string,
277
+ public isOperational = true
278
+ ) {
279
+ super(message)
280
+ Object.setPrototypeOf(this, ApiError.prototype)
281
+ }
282
+ }
283
+
284
+ export function errorHandler(error: unknown, req: Request): Response {
285
+ if (error instanceof ApiError) {
286
+ return NextResponse.json({
287
+ success: false,
288
+ error: error.message
289
+ }, { status: error.statusCode })
290
+ }
291
+
292
+ if (error instanceof z.ZodError) {
293
+ return NextResponse.json({
294
+ success: false,
295
+ error: 'Validation failed',
296
+ details: error.errors
297
+ }, { status: 400 })
298
+ }
299
+
300
+ // Log unexpected errors
301
+ console.error('Unexpected error:', error)
302
+
303
+ return NextResponse.json({
304
+ success: false,
305
+ error: 'Internal server error'
306
+ }, { status: 500 })
307
+ }
308
+
309
+ // Usage
310
+ export async function GET(request: Request) {
311
+ try {
312
+ const data = await fetchData()
313
+ return NextResponse.json({ success: true, data })
314
+ } catch (error) {
315
+ return errorHandler(error, request)
316
+ }
317
+ }
318
+ ```
319
+
320
+ ### Retry with Exponential Backoff
321
+
322
+ ```typescript
323
+ async function fetchWithRetry<T>(
324
+ fn: () => Promise<T>,
325
+ maxRetries = 3
326
+ ): Promise<T> {
327
+ let lastError: Error
328
+
329
+ for (let i = 0; i < maxRetries; i++) {
330
+ try {
331
+ return await fn()
332
+ } catch (error) {
333
+ lastError = error as Error
334
+
335
+ if (i < maxRetries - 1) {
336
+ // Exponential backoff: 1s, 2s, 4s
337
+ const delay = Math.pow(2, i) * 1000
338
+ await new Promise(resolve => setTimeout(resolve, delay))
339
+ }
340
+ }
341
+ }
342
+
343
+ throw lastError!
344
+ }
345
+
346
+ // Usage
347
+ const data = await fetchWithRetry(() => fetchFromAPI())
348
+ ```
349
+
350
+ ## Authentication & Authorization
351
+
352
+ ### JWT Token Validation
353
+
354
+ ```typescript
355
+ import jwt from 'jsonwebtoken'
356
+
357
+ interface JWTPayload {
358
+ userId: string
359
+ email: string
360
+ role: 'admin' | 'user'
361
+ }
362
+
363
+ export function verifyToken(token: string): JWTPayload {
364
+ try {
365
+ const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload
366
+ return payload
367
+ } catch (error) {
368
+ throw new ApiError(401, 'Invalid token')
369
+ }
370
+ }
371
+
372
+ export async function requireAuth(request: Request) {
373
+ const token = request.headers.get('authorization')?.replace('Bearer ', '')
374
+
375
+ if (!token) {
376
+ throw new ApiError(401, 'Missing authorization token')
377
+ }
378
+
379
+ return verifyToken(token)
380
+ }
381
+
382
+ // Usage in API route
383
+ export async function GET(request: Request) {
384
+ const user = await requireAuth(request)
385
+
386
+ const data = await getDataForUser(user.userId)
387
+
388
+ return NextResponse.json({ success: true, data })
389
+ }
390
+ ```
391
+
392
+ ### Role-Based Access Control
393
+
394
+ ```typescript
395
+ type Permission = 'read' | 'write' | 'delete' | 'admin'
396
+
397
+ interface User {
398
+ id: string
399
+ role: 'admin' | 'moderator' | 'user'
400
+ }
401
+
402
+ const rolePermissions: Record<User['role'], Permission[]> = {
403
+ admin: ['read', 'write', 'delete', 'admin'],
404
+ moderator: ['read', 'write', 'delete'],
405
+ user: ['read', 'write']
406
+ }
407
+
408
+ export function hasPermission(user: User, permission: Permission): boolean {
409
+ return rolePermissions[user.role].includes(permission)
410
+ }
411
+
412
+ export function requirePermission(permission: Permission) {
413
+ return (handler: (request: Request, user: User) => Promise<Response>) => {
414
+ return async (request: Request) => {
415
+ const user = await requireAuth(request)
416
+
417
+ if (!hasPermission(user, permission)) {
418
+ throw new ApiError(403, 'Insufficient permissions')
419
+ }
420
+
421
+ return handler(request, user)
422
+ }
423
+ }
424
+ }
425
+
426
+ // Usage - HOF wraps the handler
427
+ export const DELETE = requirePermission('delete')(
428
+ async (request: Request, user: User) => {
429
+ // Handler receives authenticated user with verified permission
430
+ return new Response('Deleted', { status: 200 })
431
+ }
432
+ )
433
+ ```
434
+
435
+ ## Rate Limiting
436
+
437
+ ### Simple In-Memory Rate Limiter
438
+
439
+ ```typescript
440
+ class RateLimiter {
441
+ private requests = new Map<string, number[]>()
442
+
443
+ async checkLimit(
444
+ identifier: string,
445
+ maxRequests: number,
446
+ windowMs: number
447
+ ): Promise<boolean> {
448
+ const now = Date.now()
449
+ const requests = this.requests.get(identifier) || []
450
+
451
+ // Remove old requests outside window
452
+ const recentRequests = requests.filter(time => now - time < windowMs)
453
+
454
+ if (recentRequests.length >= maxRequests) {
455
+ return false // Rate limit exceeded
456
+ }
457
+
458
+ // Add current request
459
+ recentRequests.push(now)
460
+ this.requests.set(identifier, recentRequests)
461
+
462
+ return true
463
+ }
464
+ }
465
+
466
+ const limiter = new RateLimiter()
467
+
468
+ export async function GET(request: Request) {
469
+ const ip = request.headers.get('x-forwarded-for') || 'unknown'
470
+
471
+ const allowed = await limiter.checkLimit(ip, 100, 60000) // 100 req/min
472
+
473
+ if (!allowed) {
474
+ return NextResponse.json({
475
+ error: 'Rate limit exceeded'
476
+ }, { status: 429 })
477
+ }
478
+
479
+ // Continue with request
480
+ }
481
+ ```
482
+
483
+ ## Background Jobs & Queues
484
+
485
+ ### Simple Queue Pattern
486
+
487
+ ```typescript
488
+ class JobQueue<T> {
489
+ private queue: T[] = []
490
+ private processing = false
491
+
492
+ async add(job: T): Promise<void> {
493
+ this.queue.push(job)
494
+
495
+ if (!this.processing) {
496
+ this.process()
497
+ }
498
+ }
499
+
500
+ private async process(): Promise<void> {
501
+ this.processing = true
502
+
503
+ while (this.queue.length > 0) {
504
+ const job = this.queue.shift()!
505
+
506
+ try {
507
+ await this.execute(job)
508
+ } catch (error) {
509
+ console.error('Job failed:', error)
510
+ }
511
+ }
512
+
513
+ this.processing = false
514
+ }
515
+
516
+ private async execute(job: T): Promise<void> {
517
+ // Job execution logic
518
+ }
519
+ }
520
+
521
+ // Usage for indexing markets
522
+ interface IndexJob {
523
+ marketId: string
524
+ }
525
+
526
+ const indexQueue = new JobQueue<IndexJob>()
527
+
528
+ export async function POST(request: Request) {
529
+ const { marketId } = await request.json()
530
+
531
+ // Add to queue instead of blocking
532
+ await indexQueue.add({ marketId })
533
+
534
+ return NextResponse.json({ success: true, message: 'Job queued' })
535
+ }
536
+ ```
537
+
538
+ ## Logging & Monitoring
539
+
540
+ ### Structured Logging
541
+
542
+ ```typescript
543
+ interface LogContext {
544
+ userId?: string
545
+ requestId?: string
546
+ method?: string
547
+ path?: string
548
+ [key: string]: unknown
549
+ }
550
+
551
+ class Logger {
552
+ log(level: 'info' | 'warn' | 'error', message: string, context?: LogContext) {
553
+ const entry = {
554
+ timestamp: new Date().toISOString(),
555
+ level,
556
+ message,
557
+ ...context
558
+ }
559
+
560
+ console.log(JSON.stringify(entry))
561
+ }
562
+
563
+ info(message: string, context?: LogContext) {
564
+ this.log('info', message, context)
565
+ }
566
+
567
+ warn(message: string, context?: LogContext) {
568
+ this.log('warn', message, context)
569
+ }
570
+
571
+ error(message: string, error: Error, context?: LogContext) {
572
+ this.log('error', message, {
573
+ ...context,
574
+ error: error.message,
575
+ stack: error.stack
576
+ })
577
+ }
578
+ }
579
+
580
+ const logger = new Logger()
581
+
582
+ // Usage
583
+ export async function GET(request: Request) {
584
+ const requestId = crypto.randomUUID()
585
+
586
+ logger.info('Fetching markets', {
587
+ requestId,
588
+ method: 'GET',
589
+ path: '/api/markets'
590
+ })
591
+
592
+ try {
593
+ const markets = await fetchMarkets()
594
+ return NextResponse.json({ success: true, data: markets })
595
+ } catch (error) {
596
+ logger.error('Failed to fetch markets', error as Error, { requestId })
597
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
598
+ }
599
+ }
600
+ ```
601
+
602
+ **Remember**: Backend patterns enable scalable, maintainable server-side applications. Choose patterns that fit your complexity level.
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: benchmark
3
+ description: |
4
+ Auto-invoke when measuring performance impact of a PR, setting up performance baselines,
5
+ investigating "feels slow" or "it's getting slower" reports, comparing stack alternatives,
6
+ or validating Core Web Vitals before a launch. Requires browser MCP or direct API access
7
+ to target environment. SKIP if neither is available — note the gap to user.
8
+ origin: ECC
9
+ allowed-tools: Read, Bash
10
+ ---
11
+
12
+ # Benchmark — Performance Baseline & Regression Detection
13
+
14
+ ## When to Use
15
+
16
+ - Before and after a PR to measure performance impact
17
+ - Setting up performance baselines for a project
18
+ - When users report "it feels slow"
19
+ - Before a launch — ensure you meet performance targets
20
+ - Comparing your stack against alternatives
21
+
22
+ ## How It Works
23
+
24
+ ### Mode 1: Page Performance
25
+
26
+ Measures real browser metrics via browser MCP:
27
+
28
+ ```
29
+ 1. Navigate to each target URL
30
+ 2. Measure Core Web Vitals:
31
+ - LCP (Largest Contentful Paint) — target < 2.5s
32
+ - CLS (Cumulative Layout Shift) — target < 0.1
33
+ - INP (Interaction to Next Paint) — target < 200ms
34
+ - FCP (First Contentful Paint) — target < 1.8s
35
+ - TTFB (Time to First Byte) — target < 800ms
36
+ 3. Measure resource sizes:
37
+ - Total page weight (target < 1MB)
38
+ - JS bundle size (target < 200KB gzipped)
39
+ - CSS size
40
+ - Image weight
41
+ - Third-party script weight
42
+ 4. Count network requests
43
+ 5. Check for render-blocking resources
44
+ ```
45
+
46
+ ### Mode 2: API Performance
47
+
48
+ Benchmarks API endpoints:
49
+
50
+ ```
51
+ 1. Hit each endpoint 100 times
52
+ 2. Measure: p50, p95, p99 latency
53
+ 3. Track: response size, status codes
54
+ 4. Test under load: 10 concurrent requests
55
+ 5. Compare against SLA targets
56
+ ```
57
+
58
+ ### Mode 3: Build Performance
59
+
60
+ Measures development feedback loop:
61
+
62
+ ```
63
+ 1. Cold build time
64
+ 2. Hot reload time (HMR)
65
+ 3. Test suite duration
66
+ 4. TypeScript check time
67
+ 5. Lint time
68
+ 6. Docker build time
69
+ ```
70
+
71
+ ### Mode 4: Before/After Comparison
72
+
73
+ Run before and after a change to measure impact:
74
+
75
+ ```
76
+ /benchmark baseline # saves current metrics
77
+ # ... make changes ...
78
+ /benchmark compare # compares against baseline
79
+ ```
80
+
81
+ Output:
82
+ ```
83
+ | Metric | Before | After | Delta | Verdict |
84
+ |--------|--------|-------|-------|---------|
85
+ | LCP | 1.2s | 1.4s | +200ms | WARNING: WARN |
86
+ | Bundle | 180KB | 175KB | -5KB | ✓ BETTER |
87
+ | Build | 12s | 14s | +2s | WARNING: WARN |
88
+ ```
89
+
90
+ ## Output
91
+
92
+ Stores baselines in `docs/benchmarks/` as JSON. Git-tracked so the team shares baselines.
93
+
94
+ ## Integration
95
+
96
+ - CI: run `/benchmark compare` on every PR
97
+ - Pair with `/canary-watch` for post-deploy monitoring
98
+ - Pair with `/browser-qa` for full pre-ship checklist