@ryuenn3123/agentic-senior-core 2.0.17 → 2.0.19

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/.windsurfrules CHANGED
@@ -1,7 +1,7 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v2.0.17
4
- Timestamp: 2026-04-08T14:58:53.570Z
3
+ Generated by Agentic-Senior-Core CLI v2.0.19
4
+ Timestamp: 2026-04-15T00:14:51.184Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
7
7
 
@@ -16,3704 +16,78 @@ Selected policy file: .agent-context/policies/llm-judge-threshold.json
16
16
  - Exception path: .agent-override.md may explicitly allow narrow deviations.
17
17
  - Scope policy: every override must include module scope, rationale, and expiry date.
18
18
 
19
- ## UNIVERSAL RULE: api-docs.md
20
- Source: .agent-context/rules/api-docs.md
21
-
22
- # API Documentation — The "Invisible API" Rule
23
-
24
- > An undocumented API is an unusable API.
25
- > If it's not in the spec, it doesn't exist.
26
-
27
- ## The Zero-Doc Death Penalty
28
-
29
- Every API endpoint MUST have machine-readable documentation that covers:
30
- 1. HTTP method + path
31
- 2. Request body schema with field descriptions and examples
32
- 3. All response codes with typed schemas
33
- 4. Authentication requirements
34
- 5. Rate limiting (if applicable)
35
-
36
- ```
37
- BANNED: An endpoint without a defined Request/Response schema.
38
- BANNED: Using generic `object` or `any` in documentation types.
39
- BANNED: "See code for details" as documentation.
40
- BANNED: Swagger UI with auto-generated summaries only (no descriptions, no examples).
41
-
42
- REQUIRED: Every field MUST have a `description` and an `example` value.
43
- REQUIRED: Every error response MUST be documented (400, 401, 403, 404, 409, 500).
44
- REQUIRED: Documentation MUST be updated in the SAME commit as the endpoint change.
45
- ```
46
-
47
- ---
48
-
49
- ## Documentation Format: OpenAPI 3.1 (Non-Negotiable)
50
-
51
- All APIs produce an OpenAPI 3.1 specification. Not 3.0, not proprietary formats, not "we'll add Swagger later."
52
-
53
- ### Why OpenAPI
54
- - Machine-readable: clients, tests, and mocks can be generated from the spec
55
- - Full JSON Schema Draft 2020-12 compatibility (3.1+ only)
56
- - Vendor-neutral: works with Scalar, Swagger UI, Redoc, Postman, Stoplight
57
- - Version-controllable: the spec is a file you can diff and review
58
-
59
- ---
60
-
61
- ## Tooling by Framework
62
-
63
- ### NestJS
64
- Use `@nestjs/swagger` with Scalar UI (not default Swagger UI — Scalar is faster and more readable).
65
-
66
- ```typescript
67
- // main.ts — Setup
68
- import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
69
- import { apiReference } from '@scalar/nestjs-api-reference';
70
-
71
- const config = new DocumentBuilder()
72
- .setTitle('Service Name')
73
- .setDescription('Brief service purpose')
74
- .setVersion('1.0')
75
- .addBearerAuth()
76
- .build();
77
-
78
- const document = SwaggerModule.createDocument(app, config);
79
-
80
- // Scalar UI at /docs
81
- app.use('/docs', apiReference({ spec: { content: document } }));
82
-
83
- // Raw spec at /api-json
84
- SwaggerModule.setup('api', app, document);
85
- ```
86
-
87
- Every controller method requires these decorators at minimum:
88
- ```typescript
89
- @ApiOperation({ summary: 'Create a user account' })
90
- @ApiBody({ type: CreateUserDto, description: 'User registration data' })
91
- @ApiResponse({ status: 201, type: UserResponseDto, description: 'User created successfully' })
92
- @ApiResponse({ status: 400, description: 'Validation error — invalid input fields' })
93
- @ApiResponse({ status: 409, description: 'Conflict — email already registered' })
94
- @Post()
95
- ```
96
-
97
- Every DTO property requires `@ApiProperty`:
98
- ```typescript
99
- export class CreateUserDto {
100
- @ApiProperty({
101
- description: 'User email address, must be unique across the system',
102
- example: 'jane.doe@example.com',
103
- })
104
- email: string;
105
-
106
- @ApiProperty({
107
- description: 'Display name shown in the UI',
108
- example: 'Jane Doe',
109
- minLength: 1,
110
- maxLength: 100,
111
- })
112
- name: string;
113
- }
114
- ```
115
-
116
- ### Next.js (App Router)
117
- Use `zod-to-openapi` or `next-swagger-doc` to generate OpenAPI from Zod schemas.
118
-
119
- ```typescript
120
- import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
121
- import { z } from 'zod';
122
-
123
- extendZodWithOpenApi(z);
124
-
125
- export const CreateUserSchema = z.object({
126
- email: z.string().email().openapi({
127
- description: 'User email address, must be unique',
128
- example: 'jane.doe@example.com',
129
- }),
130
- name: z.string().min(1).max(100).openapi({
131
- description: 'Display name shown in the UI',
132
- example: 'Jane Doe',
133
- }),
134
- }).openapi('CreateUserRequest');
135
- ```
136
-
137
- Expose the spec at `/api/docs` or `/api/openapi.json`.
138
-
139
- ### Other Frameworks (Express, Fastify, Hono)
140
- Use `swagger-jsdoc` + TSDoc comments, or `@asteasolutions/zod-to-openapi`.
141
- The output MUST be a valid OpenAPI 3.1 JSON/YAML file served at a known endpoint.
142
-
143
- ---
144
-
145
- ## Response Documentation Standard
146
-
147
- Every endpoint MUST document these response scenarios:
148
-
149
- | Status | When | Schema Required |
150
- |--------|------|----------------|
151
- | `200` | Successful retrieval or update | Yes — typed response body |
152
- | `201` | Successful creation | Yes — the created resource |
153
- | `204` | Successful deletion | No body |
154
- | `400` | Validation error | Yes — field-level error details |
155
- | `401` | Missing or invalid authentication | Fixed message, no details |
156
- | `403` | Authenticated but insufficient permissions | Fixed message |
157
- | `404` | Resource not found | Fixed message |
158
- | `409` | Conflict (duplicate resource) | Description of conflict |
159
- | `429` | Rate limit exceeded | Retry-After header |
160
- | `500` | Internal error | `{ traceId }` only — NO stack traces |
161
-
162
- ### Error Response Schema (Standardized)
163
- ```json
164
- {
165
- "error": {
166
- "code": "VALIDATION_ERROR",
167
- "message": "One or more fields are invalid",
168
- "details": [
169
- { "field": "email", "message": "Invalid email format" }
170
- ],
171
- "traceId": "req-abc-123"
172
- }
173
- }
174
- ```
175
-
176
- This schema MUST be documented in OpenAPI as a reusable component (`#/components/schemas/ErrorResponse`).
177
-
178
- ---
179
-
180
- ## Documentation Sync Rule
181
-
182
- ```
183
- Endpoint changed + docs NOT updated = PR REJECTED.
184
-
185
- The spec is a contract. If the contract is wrong, consumers will break.
186
- "I'll update the docs later" means "the docs will never be updated."
187
- ```
188
-
189
- ### Enforcement
190
- 1. API docs live next to the code (same module, same directory)
191
- 2. Docs update in the SAME commit as the endpoint change
192
- 3. CI can validate the spec: `openapi-generator validate -i openapi.json`
193
- 4. Generated clients (if any) must be regenerated after spec changes
194
-
195
- ---
196
-
197
- ## The Documentation Quality Test
198
-
199
- Open your API docs URL (`/docs`). For a randomly chosen endpoint, verify:
200
-
201
- 1. Can a developer who has never seen the codebase call this endpoint correctly? (Must be YES)
202
- 2. Are all required fields clearly marked? (Must be YES)
203
- 3. Does every field have a realistic example value? (Must be YES)
204
- 4. Are all error responses documented with their conditions? (Must be YES)
205
- 5. Can you copy the example request and it works? (Must be YES)
206
-
207
- If any answer is "no", the documentation is incomplete.
208
- ## UNIVERSAL RULE: architecture.md
209
- Source: .agent-context/rules/architecture.md
210
-
211
- # Architecture — Separation of Concerns & Structure
212
-
213
- > If your service file imports an HTTP library, your architecture is broken.
214
- > If your controller contains SQL, you've already lost.
215
-
216
- ## The Core Principle
217
-
218
- **Every layer has ONE job. Layer leaks are bugs — not "pragmatic shortcuts."**
219
-
220
- ```
221
- ┌─────────────────────────────────────────┐
222
- │ TRANSPORT / CONTROLLER │ ← Parse input, validate shape, return response
223
- │ (HTTP, CLI, WebSocket, Queue) │ ← NO business logic here. EVER.
224
- ├─────────────────────────────────────────┤
225
- │ APPLICATION / SERVICE │ ← Business rules, orchestration, transactions
226
- │ (Use cases, workflows) │ ← NO HTTP, NO SQL, NO framework imports
227
- ├─────────────────────────────────────────┤
228
- │ DOMAIN / ENTITY │ ← Pure business objects, value objects
229
- │ (Models, rules, calculations) │ ← ZERO external dependencies
230
- ├─────────────────────────────────────────┤
231
- │ INFRASTRUCTURE / REPOSITORY │ ← Database, external APIs, file system
232
- │ (Data access, adapters) │ ← NO business logic
233
- └─────────────────────────────────────────┘
234
- ```
235
-
236
- ## Layer Rules (Enforced)
237
-
238
- ### Transport Layer (Controller / Handler / Route)
239
- **Allowed:**
240
- - Parse and validate incoming request (DTO/schema validation)
241
- - Call application/service layer
242
- - Format and return HTTP response (status code, headers)
243
- - Handle authentication/authorization middleware
244
-
245
- **BANNED:**
246
- - Database queries or ORM calls
247
- - Business logic (if/else on business rules)
248
- - Direct calls to external APIs
249
- - Transaction management
250
-
251
- ### Application Layer (Service / Use Case)
252
- **Allowed:**
253
- - Orchestrate business operations
254
- - Call repository layer for data
255
- - Apply business rules and validations
256
- - Manage transactions
257
- - Emit domain events
258
-
259
- **BANNED:**
260
- - HTTP request/response objects
261
- - Framework-specific decorators (keep framework coupling minimal)
262
- - Direct SQL or raw database calls
263
- - UI/presentation logic
264
-
265
- ### Domain Layer (Entity / Value Object)
266
- **Allowed:**
267
- - Business calculations and rules
268
- - Validation of domain invariants
269
- - Type definitions and interfaces
270
-
271
- **BANNED:**
272
- - ANY external dependency (database, HTTP, framework)
273
- - Side effects (logging, API calls, file I/O)
274
- - Infrastructure concerns
275
-
276
- ### Infrastructure Layer (Repository / Adapter)
277
- **Allowed:**
278
- - Database queries (SQL, ORM, document queries)
279
- - External API calls (wrapped in adapters)
280
- - File system operations
281
- - Cache operations
282
-
283
- **BANNED:**
284
- - Business logic (no if/else on business rules in queries)
285
- - HTTP response formatting
286
- - Direct exposure to transport layer
287
-
288
- ---
289
-
290
- ## Dependency Direction
291
-
292
- Dependencies flow **inward only**:
293
-
294
- ```
295
- Transport → Application → Domain ← Infrastructure
296
-
297
- Infrastructure
298
-
299
- NEVER: Domain → Infrastructure (use interfaces/ports)
300
- NEVER: Application → Transport
301
- NEVER: Infrastructure → Application (except through interfaces)
302
- ```
303
-
304
- The Domain layer depends on NOTHING. Everything depends on the Domain.
305
-
306
- ---
307
-
308
- ## Default Architecture: Modular Monolith
309
-
310
- Start with a **Modular Monolith**. Do NOT start with microservices.
311
-
312
- **Switch to microservices ONLY if 2+ of these triggers exist:**
313
- 1. Frequent deploy conflicts across domains (teams blocking each other)
314
- 2. Clear scale mismatch (one module needs 100x resources of another)
315
- 3. Team ownership collision (multiple teams editing same module)
316
- 4. Fault isolation requirement (one module crashing must not kill others)
317
- 5. Stable contracts with clear data boundaries already exist
318
-
319
- If these triggers don't exist, microservices are **premature complexity**.
320
-
321
- ---
322
-
323
- ## Project Structure: Feature-Based Grouping
324
-
325
- ### ❌ BANNED: Technical Grouping
326
- ```
327
- src/
328
- controllers/ ← 50 controllers in one flat folder?
329
- userController.ts
330
- orderController.ts
331
- paymentController.ts
332
- services/ ← Good luck finding related code
333
- userService.ts
334
- orderService.ts
335
- repositories/
336
- userRepository.ts
337
- orderRepository.ts
338
- ```
339
-
340
- ### ✅ REQUIRED: Feature/Domain Grouping
341
- ```
342
- src/
343
- modules/ ← Backend
344
- user/
345
- user.controller.ts ← Transport
346
- user.service.ts ← Application
347
- user.repository.ts ← Infrastructure
348
- user.entity.ts ← Domain
349
- user.dto.ts ← Data Transfer Objects
350
- user.module.ts ← Module registration
351
- __tests__/
352
- user.service.test.ts
353
- order/
354
- order.controller.ts
355
- order.service.ts
356
- ...
357
- shared/ ← Cross-cutting concerns
358
- config/
359
- errors/
360
- logging/
361
- middleware/
362
-
363
- src/
364
- features/ ← Frontend
365
- payment/
366
- api/ ← HTTP client + DTOs
367
- hooks/ ← React hooks / state
368
- components/ ← UI components
369
- types/ ← Type definitions
370
- utils/ ← Feature-specific utils
371
- index.ts ← Public API barrel
372
- components/
373
- ui/ ← Shared UI primitives
374
- layout/ ← Layout components
375
- lib/ ← Shared utilities
376
- config/ ← App configuration
377
- ```
378
-
379
- ---
380
-
381
- ## Module Communication
382
-
383
- ### Within a Monolith
384
- Modules communicate through **public interfaces only**:
385
- ```
386
- // ✅ CORRECT: Import from module's public API
387
- import { UserService } from '@/modules/user';
388
-
389
- // ❌ BANNED: Reach into another module's internals
390
- import { UserRepository } from '@/modules/user/user.repository';
391
- ```
392
-
393
- ### Between Services (if microservices)
394
- - Use well-defined contracts (REST, gRPC, events)
395
- - Never share databases between services
396
- - Define schemas at boundaries (Protobuf, JSON Schema, Zod)
397
-
398
- ---
399
-
400
- ## The Architecture Smell Test
401
-
402
- Ask yourself these questions. If ANY answer is "yes", your architecture is broken:
403
-
404
- 1. Can I change the database without touching business logic? (Must be YES)
405
- 2. Can I switch from REST to GraphQL without rewriting services? (Must be YES)
406
- 3. Can I test business logic without a running database? (Must be YES)
407
- 4. Does each module have a clear, single responsibility? (Must be YES)
408
- 5. Can a new developer find all related code in one directory? (Must be YES)
409
- ## UNIVERSAL RULE: database-design.md
410
- Source: .agent-context/rules/database-design.md
411
-
412
- # Database Design — Schema Is Your Foundation
413
-
414
- > A poorly designed schema is a bug factory.
415
- > You can fix bad code in hours. A bad schema takes weeks.
416
-
417
- ## Normalization Rules
418
-
419
- ### Third Normal Form (3NF) is the Default
420
-
421
- ```
422
- ❌ BANNED: Flat tables with repeated data
423
- Users:
424
- | id | name | order_id | order_total | product_name |
425
- | 1 | Jane | 101 | 99.99 | Widget |
426
- | 1 | Jane | 102 | 49.99 | Gadget |
427
- → name is duplicated, update anomalies guaranteed
428
-
429
- ✅ REQUIRED: Normalized to 3NF
430
- Users: | id | name | email |
431
- Orders: | id | user_id | total | status |
432
- Products: | id | name | price |
433
- OrderItems: | order_id | product_id | quantity |
434
- → Each fact is stored exactly once
435
- ```
436
-
437
- ### When to Denormalize
438
-
439
- Denormalization is allowed ONLY with documented justification:
440
-
441
- 1. **Read-heavy query** that joins 4+ tables and is called >1000x/sec
442
- 2. **Reporting/analytics** where query speed matters more than write consistency
443
- 3. **CQRS read model** that is purpose-built for a specific query
444
-
445
- ```
446
- REQUIRED for every denormalization:
447
- - Document WHY (link to performance evidence)
448
- - Document HOW it stays in sync (trigger, event, scheduled job)
449
- - Add a comment in the schema: "Denormalized for [query name], synced by [mechanism]"
450
- ```
451
-
452
- ---
453
-
454
- ## Indexing Strategy
455
-
456
- ### Rules of Indexing
457
-
458
- 1. **Every foreign key gets an index** — joins on unindexed FKs are full table scans
459
- 2. **Every WHERE clause in a frequent query gets evaluated** — if the column appears in WHERE and the table has >10K rows, consider an index
460
- 3. **Composite indexes matter** — `INDEX(status, created_at)` ≠ `INDEX(created_at, status)`. Column order follows the query pattern (most selective first, or matching WHERE + ORDER BY)
461
- 4. **Covering indexes** — include frequently selected columns to avoid table lookups
462
-
463
- ### What to Index
464
-
465
- | Pattern | Index Type | Example |
466
- |---------|-----------|---------|
467
- | FK lookups | B-tree (default) | `orders.user_id` |
468
- | Status + date filters | Composite | `INDEX(status, created_at)` |
469
- | Full-text search | Full-text / GIN | `products.description` |
470
- | JSON queries | GIN (PostgreSQL) | `metadata->>'type'` |
471
- | Unique constraints | Unique | `users.email` |
472
- | Geospatial | Spatial / GiST | `locations.coordinates` |
473
-
474
- ### What NOT to Index
475
-
476
- - Columns with very low cardinality on small tables (`is_active` with 2 values on 100 rows)
477
- - Tables with <1000 rows (index overhead > benefit)
478
- - Write-heavy tables where every INSERT updates 10+ indexes
479
- - Columns never used in WHERE, JOIN, or ORDER BY
480
-
481
- ### Index Monitoring
482
-
483
- ```
484
- REQUIRED:
485
- - Identify unused indexes monthly → DROP them (they slow writes)
486
- - Identify missing indexes → EXPLAIN ANALYZE on slow queries
487
- - Track index size vs table size → alarming if indexes > 3x table size
488
- ```
489
-
490
- ---
491
-
492
- ## Migration Standards
493
-
494
- ### Rules
495
-
496
- 1. **Every schema change is a migration** — never modify production schemas manually
497
- 2. **Migrations are versioned and sequential** — `001_create_users.sql`, `002_add_email_index.sql`
498
- 3. **Migrations are idempotent** — running twice produces the same result
499
- 4. **Migrations are reversible** — every UP has a DOWN (or document why rollback is impossible)
500
- 5. **Migrations run in CI** — test against a real database, not just syntax checks
501
-
502
- ### Safe Migration Patterns
503
-
504
- ```
505
- ✅ SAFE (no downtime):
506
- 1. ADD COLUMN with default (nullable or with DEFAULT)
507
- 2. CREATE INDEX CONCURRENTLY
508
- 3. ADD new table
509
- 4. Rename via view + synonym (transitional)
510
-
511
- ❌ DANGEROUS (requires planned downtime or careful orchestration):
512
- 1. DROP COLUMN (deploy code changes removing column usage FIRST)
513
- 2. RENAME COLUMN (use gradual rename: add new → copy data → remove old)
514
- 3. ALTER COLUMN TYPE (may lock table on large datasets)
515
- 4. DROP TABLE (ensure no code references remain)
516
- ```
517
-
518
- ### The Expand-Contract Pattern
519
-
520
- For breaking schema changes in zero-downtime deployments:
521
-
522
- ```
523
- Phase 1 (Expand): Add new column/table alongside old one
524
- → Code writes to BOTH old and new
525
- Phase 2 (Migrate): Backfill data from old to new
526
- → Verify data consistency
527
- Phase 3 (Contract): Remove old column/table
528
- → Code reads/writes only new
529
- ```
530
-
531
- ---
532
-
533
- ## Data Type Selection
534
-
535
- ### Use the Right Type
536
-
537
- | Data | ❌ Wrong | ✅ Right | Why |
538
- |------|---------|---------|-----|
539
- | Money | `FLOAT` | `DECIMAL(19,4)` or integer cents | Floating point arithmetic is imprecise |
540
- | UUID | `VARCHAR(36)` | `UUID` native type | 16 bytes vs 36 bytes, indexing is faster |
541
- | Timestamps | `VARCHAR` | `TIMESTAMPTZ` | Timezone-aware, sortable, comparable |
542
- | IP addresses | `VARCHAR(45)` | `INET` (PostgreSQL) | Validation built-in, range queries |
543
- | Boolean | `INT` (0/1) | `BOOLEAN` | Semantic clarity |
544
- | Enums | `VARCHAR` | Database ENUM or CHECK constraint | Prevents invalid values |
545
- | JSON blobs | `TEXT` | `JSONB` (PostgreSQL) | Indexable, queryable, validated |
546
-
547
- ### Column Naming
548
-
549
- ```
550
- ✅ REQUIRED:
551
- - snake_case for all column names
552
- - Descriptive: created_at, updated_at, deleted_at (not ts, mod, del)
553
- - Foreign keys: {referenced_table_singular}_id (e.g., user_id, order_id)
554
- - Boolean columns: is_active, has_verified_email, can_edit
555
- - Timestamps: {verb}_at (created_at, verified_at, shipped_at)
556
- - Amounts: {noun}_amount or {noun}_cents (total_amount, tax_cents)
557
- ```
558
-
559
- ---
560
-
561
- ## Query Design Rules
562
-
563
- ### Pagination (Mandatory for Lists)
564
-
565
- ```
566
- ❌ BANNED:
567
- SELECT * FROM orders;
568
- -- Returns 2 million rows, OOM crash
569
-
570
- ✅ REQUIRED: Offset pagination (simple, okay for < 100K rows)
571
- SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 40;
572
-
573
- ✅ PREFERRED: Cursor pagination (performant at any scale)
574
- SELECT * FROM orders WHERE id > :last_seen_id ORDER BY id LIMIT 20;
575
- ```
576
-
577
- **Rule:** Use cursor-based pagination for tables > 100K rows. Offset pagination degrades linearly with OFFSET value.
578
-
579
- ### Soft Deletes vs Hard Deletes
580
-
581
- ```
582
- Use soft deletes (deleted_at column) when:
583
- - Legal/compliance requires data retention
584
- - Users might want to "undelete"
585
- - Related data would break without the parent record
586
-
587
- Use hard deletes when:
588
- - GDPR/privacy requires actual data removal
589
- - Data has no business value after deletion
590
- - Storage cost of keeping data outweighs benefit
591
-
592
- If soft deleting:
593
- - Add deleted_at to ALL queries: WHERE deleted_at IS NULL
594
- - Add a partial index: WHERE deleted_at IS NULL (for performance)
595
- - Schedule periodic hard-delete of old soft-deleted records
596
- ```
597
-
598
- ---
599
-
600
- ## The Database Design Checklist
601
-
602
- Before any schema goes to production:
603
-
604
- - [ ] Schema is normalized to 3NF (denormalization documented if present)
605
- - [ ] All foreign keys have indexes
606
- - [ ] Primary keys use appropriate type (UUID or BIGINT)
607
- - [ ] Timestamps use TIMESTAMPTZ (not VARCHAR)
608
- - [ ] Money uses DECIMAL or integer cents (never FLOAT)
609
- - [ ] Migration is versioned, reversible, and tested in CI
610
- - [ ] All list queries have LIMIT (pagination implemented)
611
- - [ ] Indexes justified with EXPLAIN ANALYZE on expected queries
612
- - [ ] Column naming follows convention (snake_case, descriptive)
613
- - [ ] Soft delete vs hard delete decision documented
614
- ## UNIVERSAL RULE: efficiency-vs-hype.md
615
- Source: .agent-context/rules/efficiency-vs-hype.md
616
-
617
- # Efficiency vs. Hype — Choose Stable, Not Shiny
618
-
619
- > Every dependency is a liability. Every `npm install` is a trust decision.
620
- > The best dependency is the one you don't need.
621
-
622
- ## The Dependency Decision Framework
623
-
624
- Before adding ANY dependency, answer these 5 questions:
625
-
626
- ### 1. Can You Do It Without a Library? (The stdlib-first Rule)
627
- ```
628
- ❌ OVER-ENGINEERING:
629
- npm install is-odd # 1 line of code: n % 2 !== 0
630
- npm install left-pad # Already in String.prototype.padStart
631
- npm install is-number # typeof x === 'number' && !isNaN(x)
632
- npm install array-flatten # Array.prototype.flat() exists since ES2019
633
-
634
- ✅ USE THE LANGUAGE:
635
- const isOdd = (n: number) => n % 2 !== 0;
636
- const padded = str.padStart(10, '0');
637
- const flat = nested.flat(Infinity);
638
- ```
639
-
640
- **Dependency Defense:** If the user asks to install a new library, or if you feel the need to use one, evaluate it against the "stdlib-first" rule. If the functionality can be implemented safely in under 20 lines of code, write it yourself. If a dependency is strictly necessary, you MUST justify it by providing its bundle size, maintenance status, and why the standard library is insufficient.
641
-
642
- ### 2. Is It Maintained? (The Pulse Check)
643
- | Signal | 🟢 Healthy | 🔴 Dead |
644
- |--------|-----------|---------|
645
- | Last commit | < 6 months ago | > 2 years ago |
646
- | Open issues | Actively triaged | 500+ unread |
647
- | Downloads/week | Growing or stable | Declining |
648
- | Bus factor | > 3 maintainers | 1 maintainer, inactive |
649
- | Security | No known CVEs | Unpatched vulnerabilities |
650
-
651
- **Rule:** If a library has a bus factor of 1 and no commit in 12+ months, it is a risk. Find an alternative or vendor-fork it.
652
-
653
- ### 3. What's the Cost? (The Weight Check)
654
- ```
655
- Before: npm install moment # 289KB minified, entire library for date formatting
656
- After: npm install date-fns # 12KB for just the functions you use (tree-shakeable)
657
- Better: Intl.DateTimeFormat # 0KB — built into the runtime
658
- ```
659
-
660
- **Rule:** Check the bundle impact. Use [bundlephobia.com](https://bundlephobia.com) for JS packages. If a library adds > 50KB to your bundle for a simple feature, find a lighter alternative or implement it yourself.
661
-
662
- ### 4. Is It the Community Standard? (The Ecosystem Rule)
663
- Prefer packages that the ecosystem has settled on:
664
-
665
- | Need | ✅ Standard | ❌ Avoid |
666
- |------|-----------|---------|
667
- | HTTP client (Node) | `undici` (built-in) / native `fetch` / `ky` | `axios` (declining, CVE-2025-58754, bloated) |
668
- | Validation | `zod` | `joi` (heavier), `yup` (less type-safe) |
669
- | ORM (Node) | `prisma`, `drizzle` | `sequelize` (legacy API), `typeorm` (decorator hell) |
670
- | Date handling | `date-fns`, `dayjs`, `Temporal` (when stable) | `moment` (deprecated, massive) |
671
- | Testing | `vitest` (new projects), `jest` (existing) | `mocha` + `chai` + `sinon` (3 deps for what 1 does) |
672
- | Password hashing | `argon2` (OWASP primary), `bcrypt` (legacy) | Custom crypto (NEVER) |
673
- | Env loading | `dotenv` (if needed) | Custom `.env` parser |
674
-
675
- **Note:** These are current recommendations as of March 2026. Evaluate against this framework; don't blindly follow.
676
-
677
- ### 5. Can You Remove It Later? (The Exit Strategy)
678
- ```
679
- ❌ HIGH LOCK-IN:
680
- // Decorators from a specific framework scattered across 200 files
681
- @Controller() @Injectable() @Guard() // In every file — framework is your entire architecture
682
-
683
- ✅ LOW LOCK-IN:
684
- // Framework stays at the edges; business logic is framework-free
685
- // Switching from Express to Fastify only changes the transport layer
686
- ```
687
-
688
- **Rule:** Wrap external dependencies that touch > 5 files. If you need to replace a library, it should only affect the wrapper, not your entire codebase.
689
-
690
- ---
691
-
692
- ## The Hype Trap (AI-Generated Code Alert)
693
-
694
- AI agents love suggesting trendy libraries because they appear frequently in training data. Watch for:
695
-
696
- ### Red Flags
697
- 1. **"Just install X"** without explaining why → ASK: Can I do this with the stdlib?
698
- 2. **Library does one thing** that's 5 lines of code → REJECT: Write it yourself
699
- 3. **Library is in alpha/beta** with < 1 year of releases → REJECT: Not production-ready
700
- 4. **Library has a cooler API** but the current one works fine → REJECT: "Cool" is not a business requirement
701
- 5. **"Everyone is using it"** → SO WHAT: Popularity is not a quality signal
702
-
703
- ### The Agent Must Justify Dependencies
704
- When an AI agent suggests adding a new dependency, it MUST provide:
705
- ```
706
- 📦 DEPENDENCY JUSTIFICATION
707
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━
708
- Package: [name@version]
709
- Purpose: [what it does in 1 sentence]
710
- Stdlib Alternative: [why it's insufficient — or "none"]
711
- Bundle Size: [minified + gzipped]
712
- Maintenance: [last release date, maintainer count]
713
- Lock-in Risk: [low/medium/high — how many files would it touch]
714
- Exit Strategy: [how to remove it if needed]
715
- ```
716
-
717
- ---
718
-
719
- ## Dependency Update Strategy
720
-
721
- 1. **Pin exact versions** in lockfiles (already default with package-lock.json, bun.lockb)
722
- 2. **Review changelogs** before major updates — never blindly `npm update`
723
- 3. **Update in isolation** — one dependency per PR, with tests passing
724
- 4. **Security patches immediately** — don't wait for the sprint
725
- 5. **Monthly audit** — run `npm audit` / `bun audit` and address findings
726
-
727
- ---
728
-
729
- ## The Zero-Dependency Ideal
730
-
731
- The best packages are those with zero or minimal dependencies themselves:
732
- - `zod` — 0 dependencies ✅
733
- - `date-fns` — 0 dependencies ✅
734
- - `nanoid` — 0 dependencies ✅
735
- - `bcrypt` — 1 native dependency (justified for crypto) ✅
736
-
737
- A package that pulls in 50 transitive dependencies for a simple feature is a supply chain attack waiting to happen.
738
-
739
- ---
740
-
741
- ## Quick Decision Tree
742
-
743
- ```
744
- Do I need this functionality?
745
- → No → Don't install anything
746
- → Yes ↓
747
-
748
- Can I write it in < 20 lines?
749
- → Yes → Write it yourself
750
- → No ↓
751
-
752
- Does the language/runtime provide it?
753
- → Yes → Use the built-in
754
- → No ↓
755
-
756
- Is there a well-maintained, lightweight, community-standard package?
757
- → Yes → Install it, add justification comment
758
- → No → Build a minimal internal implementation, consider vendoring
759
- ```
760
- ## UNIVERSAL RULE: error-handling.md
761
- Source: .agent-context/rules/error-handling.md
762
-
763
- # Error Handling — Never Swallow, Always Context
764
-
765
- > A swallowed error is a silent production incident waiting to happen.
766
- > When it explodes at 3 AM, you won't know where to look.
767
-
768
- ## The Three Commandments
769
-
770
- 1. **Never swallow an error.** Every error must be logged, re-thrown, or explicitly handled.
771
- 2. **Always add context.** A stack trace is not enough — include WHAT was happening and WITH WHAT data.
772
- 3. **Fail fast at boundaries.** Validate early, reject bad data before it travels deep into the system.
773
-
774
- ---
775
-
776
- ## Swallowed Error Detection (Instant Rejection)
777
-
778
- ```
779
- ❌ DEATH PENALTY: Empty catch block
780
- try {
781
- await processPayment(order);
782
- } catch (error) {
783
- // silently swallowed — payment may have failed, user has no idea
784
- }
785
-
786
- ❌ DEATH PENALTY: Catch and log only (no re-throw or recovery)
787
- try {
788
- await processPayment(order);
789
- } catch (error) {
790
- console.log('error:', error); // Logged to a void nobody reads
791
- // Execution continues as if nothing happened
792
- }
793
-
794
- ✅ CORRECT: Handle, log with context, re-throw or recover
795
- try {
796
- await processPayment(order);
797
- } catch (error) {
798
- logger.error('Payment processing failed', {
799
- orderId: order.id,
800
- userId: order.userId,
801
- amount: order.total,
802
- error: error instanceof Error ? error.message : String(error),
803
- });
804
- throw new PaymentFailedError(order.id, { cause: error });
805
- }
806
- ```
807
-
808
- ---
809
-
810
- ## Typed Error Codes
811
-
812
- ### Rule: Use Explicit Error Types, Not Generic Errors
813
-
814
- ```
815
- ❌ BANNED: Generic errors
816
- throw new Error('Not found');
817
- throw new Error('Permission denied');
818
- throw new Error('Invalid input');
819
-
820
- ✅ REQUIRED: Typed, domain-specific errors
821
- throw new NotFoundError('User', userId);
822
- throw new ForbiddenError('Cannot delete other user\'s order');
823
- throw new ValidationError('Email format is invalid', { field: 'email' });
824
- ```
825
-
826
- ### Error Class Pattern (Any Language)
827
- ```typescript
828
- // Base application error
829
- class AppError extends Error {
830
- constructor(
831
- message: string,
832
- public readonly code: string,
833
- public readonly statusCode: number,
834
- public readonly context?: Record<string, unknown>,
835
- ) {
836
- super(message);
837
- this.name = this.constructor.name;
838
- }
839
- }
840
-
841
- // Specific errors
842
- class NotFoundError extends AppError {
843
- constructor(resource: string, id: string | number) {
844
- super(`${resource} not found: ${id}`, 'NOT_FOUND', 404, { resource, id });
845
- }
846
- }
847
-
848
- class ValidationError extends AppError {
849
- constructor(message: string, context?: Record<string, unknown>) {
850
- super(message, 'VALIDATION_ERROR', 400, context);
851
- }
852
- }
853
-
854
- class ForbiddenError extends AppError {
855
- constructor(reason: string) {
856
- super(reason, 'FORBIDDEN', 403);
857
- }
858
- }
859
- ```
860
-
861
- ---
862
-
863
- ## Structured Logging
864
-
865
- ### Rule: Logs Must Include Context
866
-
867
- ```
868
- ❌ USELESS:
869
- logger.error('Something went wrong');
870
- logger.info('Processing...');
871
- console.log(error);
872
-
873
- ✅ USEFUL:
874
- logger.error('Order processing failed', {
875
- traceId: req.traceId,
876
- userId: currentUser.id,
877
- orderId: order.id,
878
- action: 'processOrder',
879
- error: error.message,
880
- stack: error.stack,
881
- });
882
-
883
- logger.info('Payment completed', {
884
- traceId: req.traceId,
885
- orderId: order.id,
886
- amount: payment.amount,
887
- provider: payment.provider,
888
- durationMs: Date.now() - startTime,
889
- });
890
- ```
891
-
892
- ### Required Log Fields
893
- | Field | When | Purpose |
894
- |-------|------|---------|
895
- | `traceId` / `requestId` | Always (in request context) | Correlate logs across services |
896
- | `userId` | When authenticated | Who triggered the action |
897
- | `action` | Always | What was happening |
898
- | `error` + `stack` | On errors | What went wrong |
899
- | `durationMs` | On slow operations | Performance visibility |
900
-
901
- ---
902
-
903
- ## Error Response Format (APIs)
904
-
905
- ### Rule: NEVER Leak Internal Details
906
-
907
- ```
908
- ❌ BANNED response to client:
909
- {
910
- "error": "ER_NO_SUCH_TABLE: Table 'mydb.user_sessions' doesn't exist",
911
- "stack": "Error: at Query.execute (/app/node_modules/mysql..."
912
- }
913
- // Congratulations, you just told the attacker your DB name and framework
914
-
915
- ✅ REQUIRED response to client:
916
- {
917
- "error": {
918
- "code": "INTERNAL_ERROR",
919
- "message": "An unexpected error occurred. Please try again.",
920
- "traceId": "abc-123-def"
921
- }
922
- }
923
- // Internal details go to your logs, not to the client
924
- ```
925
-
926
- ### Error Response Mapping
927
- | Internal Error | HTTP Status | Client Message |
928
- |---------------|-------------|----------------|
929
- | `ValidationError` | 400 | Show specific field errors |
930
- | `AuthenticationError` | 401 | "Invalid credentials" (never specify which field) |
931
- | `ForbiddenError` | 403 | "Insufficient permissions" |
932
- | `NotFoundError` | 404 | "Resource not found" |
933
- | `ConflictError` | 409 | "Resource already exists" |
934
- | `RateLimitError` | 429 | "Too many requests" |
935
- | Unhandled errors | 500 | "Internal error" + traceId for support |
936
-
937
- ---
938
-
939
- ## Fail-Fast at Boundaries
940
-
941
- ```
942
- // ✅ Validate at the entry point, fail before going deeper
943
- async function createOrder(req: Request) {
944
- // Step 1: Validate input IMMEDIATELY
945
- const parsed = CreateOrderSchema.safeParse(req.body);
946
- if (!parsed.success) {
947
- throw new ValidationError('Invalid order data', parsed.error.flatten());
948
- }
949
-
950
- // Step 2: Now use the validated, typed data
951
- const order = await orderService.create(parsed.data);
952
- return order;
953
- }
954
-
955
- // ❌ Don't let bad data travel deep before failing
956
- async function createOrder(req: Request) {
957
- const data = req.body; // Unvalidated!
958
- const order = await orderService.create(data);
959
- // Service calls repository, repository tries to insert...
960
- // Database throws a cryptic constraint violation 5 layers deep
961
- }
962
- ```
963
-
964
- ---
965
-
966
- ## Retry Strategy
967
-
968
- When retrying failed operations:
969
-
970
- 1. **Only retry transient errors** (network timeouts, 503s) — NEVER retry validation errors or auth failures
971
- 2. **Use exponential backoff** — 100ms → 200ms → 400ms → 800ms
972
- 3. **Set a maximum retry count** (typically 3)
973
- 4. **Log every retry attempt** with attempt number and error
974
- 5. **Add jitter** to prevent thundering herd
975
-
976
- ```typescript
977
- async function withRetry<T>(
978
- operation: () => Promise<T>,
979
- maxAttempts: number = 3,
980
- baseDelayMs: number = 100,
981
- ): Promise<T> {
982
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
983
- try {
984
- return await operation();
985
- } catch (error) {
986
- if (attempt === maxAttempts || !isTransientError(error)) {
987
- throw error;
988
- }
989
- const delay = baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100;
990
- logger.warn('Retrying operation', { attempt, maxAttempts, delayMs: delay });
991
- await sleep(delay);
992
- }
993
- }
994
- throw new Error('Unreachable');
995
- }
996
- ```
997
- ## UNIVERSAL RULE: event-driven.md
998
- Source: .agent-context/rules/event-driven.md
999
-
1000
- # Event-Driven Architecture — React to Facts, Don't Poll for Changes
1001
-
1002
- > If your services are constantly asking "Did anything change?", your architecture is broken.
1003
- > Events are the nervous system of a distributed system.
1004
-
1005
- ## When to Use Event-Driven Architecture
1006
-
1007
- ### Use Events When:
1008
- 1. Multiple services need to react to the same state change
1009
- 2. You need temporal decoupling (producer doesn't wait for consumer)
1010
- 3. Audit trail / event history is a business requirement
1011
- 4. Systems need to scale producers and consumers independently
1012
- 5. Eventual consistency is acceptable
1013
-
1014
- ### Don't Use Events When:
1015
- - You need an immediate, synchronous response
1016
- - The system is a simple CRUD application with 1-2 services
1017
- - You can't invest in proper infrastructure (message broker, monitoring)
1018
- - Your team has no experience with async debugging
1019
-
1020
- ---
1021
-
1022
- ## Core Patterns
1023
-
1024
- ### 1. Pub/Sub (Publish-Subscribe)
1025
-
1026
- Producer emits an event. Zero or more consumers react independently.
1027
-
1028
- ```
1029
- ┌──────────┐
1030
- │ Consumer A│ (Send email)
1031
- └────▲──────┘
1032
- ┌──────────┐ │
1033
- │ Producer │──→ EVENT BUS ──→┌──────────┐
1034
- │ (Order │ │ │ Consumer B│ (Update inventory)
1035
- │ Service) │ │ └──────────┘
1036
- └──────────┘ │
1037
- ┌───▼──────┐
1038
- │ Consumer C│ (Analytics)
1039
- └──────────┘
1040
- ```
1041
-
1042
- **Rules:**
1043
- - Producer does NOT know about consumers (fire-and-forget)
1044
- - Each consumer processes independently (failure in one doesn't affect others)
1045
- - Consumers MUST be idempotent (same event processed twice = same result)
1046
-
1047
- ### 2. CQRS (Command Query Responsibility Segregation)
1048
-
1049
- Separate the write model (commands) from the read model (queries).
1050
-
1051
- ```
1052
- ┌─────────────┐ ┌─────────────┐
1053
- │ Write Side │ Events │ Read Side │
1054
- │ (Commands) │ ──────────────→ │ (Queries) │
1055
- │ │ │ │
1056
- │ Rich domain │ │ Denormalized │
1057
- │ model │ │ read models │
1058
- │ Normalized │ │ Optimized │
1059
- │ schema │ │ for queries │
1060
- └─────────────┘ └─────────────┘
1061
- ```
1062
-
1063
- **When to use CQRS:**
1064
- - Read/write ratio is heavily skewed (100:1 reads to writes)
1065
- - Read and write models have fundamentally different shapes
1066
- - You need different scaling for reads vs writes
1067
-
1068
- **When NOT to use CQRS:**
1069
- - Simple CRUD with no complex queries
1070
- - Read and write models are identical (just use a repository)
1071
- - You don't have the team capacity to maintain two models
1072
-
1073
- ### 3. Event Sourcing
1074
-
1075
- Store the full history of state changes as immutable events, not just the current state.
1076
-
1077
- ```
1078
- Traditional: User { name: "Jane", email: "jane@new.com" }
1079
- → You only know the current state
1080
-
1081
- Event Sourced:
1082
- 1. UserCreated { name: "Jane", email: "jane@old.com" }
1083
- 2. EmailChanged { email: "jane@mid.com" }
1084
- 3. EmailChanged { email: "jane@new.com" }
1085
- → You know the full history, can rebuild any point in time
1086
- ```
1087
-
1088
- **When to use Event Sourcing:**
1089
- - Audit trail is a regulatory requirement (finance, healthcare)
1090
- - "Time travel" queries are needed (what was the state on March 1?)
1091
- - Complex domain with many state transitions
1092
- - Combined with CQRS for complex read requirements
1093
-
1094
- **When NOT to use Event Sourcing:**
1095
- - Simple CRUD applications (overkill)
1096
- - Team has no experience with event stores
1097
- - No clear business need for historical state
1098
-
1099
- ---
1100
-
1101
- ## Event Design Rules
1102
-
1103
- ### Naming: Past Tense, Domain Language
1104
-
1105
- ```
1106
- ❌ BANNED:
1107
- "UpdateOrder" → Commands, not events
1108
- "ORDER_UPDATE" → Screaming snake in an event name
1109
- "data" → Meaningless
1110
-
1111
- ✅ REQUIRED:
1112
- "OrderPlaced" → Past tense, describes what happened
1113
- "PaymentProcessed" → Specific, clear domain action
1114
- "InventoryReserved" → Business language, not technical
1115
- ```
1116
-
1117
- ### Event Schema: Include Context
1118
-
1119
- ```typescript
1120
- // ❌ BANNED: Minimal event with no context
1121
- { type: "OrderPlaced", orderId: "123" }
1122
-
1123
- // ✅ REQUIRED: Rich event with all necessary context
1124
- {
1125
- eventId: "evt_abc123", // Unique, for idempotency
1126
- eventType: "OrderPlaced", // What happened
1127
- aggregateId: "order_123", // Which entity
1128
- aggregateType: "Order", // Entity type
1129
- timestamp: "2026-03-11T...", // When it happened
1130
- version: 1, // Schema version
1131
- correlationId: "req_xyz", // Trace across services
1132
- causationId: "cmd_456", // What caused this event
1133
- data: { // The event payload
1134
- orderId: "order_123",
1135
- userId: "user_789",
1136
- items: [...],
1137
- totalAmount: 99.99,
1138
- currency: "USD"
1139
- },
1140
- metadata: { // Operational metadata
1141
- producerService: "order-service",
1142
- producerVersion: "2.1.0"
1143
- }
1144
- }
1145
- ```
1146
-
1147
- ### Event Versioning
1148
-
1149
- Events are immutable — once published, they can't change. Handle evolution with:
1150
-
1151
- 1. **Weak schema (preferred):** Consumers ignore unknown fields, use defaults for missing fields
1152
- 2. **Upcasting:** Transform old events to new schema on read
1153
- 3. **New event type:** If the change is breaking, create `OrderPlacedV2`
1154
-
1155
- ```
1156
- BANNED: Changing the schema of an existing event in a breaking way
1157
- BANNED: Removing fields from events
1158
- ALLOWED: Adding optional fields with defaults
1159
- ALLOWED: Creating a new event type for breaking changes
1160
- ```
1161
-
1162
- ---
1163
-
1164
- ## Infrastructure Choices
1165
-
1166
- | Need | Recommended | Avoid |
1167
- |------|-----------|-------|
1168
- | General pub/sub | Apache Kafka, NATS, RabbitMQ | Custom TCP sockets |
1169
- | Cloud-native | AWS EventBridge, GCP Pub/Sub, Azure Service Bus | Polling a database table |
1170
- | Simple/local | Redis Streams, NATS | ZeroMQ for production events |
1171
- | Event store | EventStoreDB, Kafka (with compaction) | Relational DB as event store (unless simple) |
1172
-
1173
- ---
1174
-
1175
- ## Reliability Patterns
1176
-
1177
- ### Outbox Pattern (Transactional Events)
1178
-
1179
- Ensure events are published reliably alongside database writes:
1180
-
1181
- ```
1182
- 1. Write to database AND outbox table in a single transaction
1183
- 2. Background process reads outbox and publishes to message broker
1184
- 3. Mark outbox entry as published
1185
-
1186
- This guarantees: if the DB write succeeds, the event WILL be published.
1187
- No more "DB updated but event lost" bugs.
1188
- ```
1189
-
1190
- ### Dead Letter Queue (DLQ)
1191
-
1192
- Messages that fail processing after N retries go to a DLQ:
1193
- - Monitor DLQ size — it should be near zero
1194
- - Alert when DLQ grows
1195
- - Investigate and reprocess failed messages
1196
- - Never ignore a growing DLQ
1197
-
1198
- ### Idempotency (Non-Negotiable)
1199
-
1200
- ```
1201
- EVERY consumer MUST handle duplicate events safely.
1202
-
1203
- Techniques:
1204
- 1. Idempotency key: Store processed eventIds, skip if seen
1205
- 2. Natural idempotency: Operations that are naturally safe to repeat
1206
- (e.g., SET status = 'paid' is idempotent; INCREMENT balance is NOT)
1207
- 3. Optimistic locking: Use version numbers to detect conflicts
1208
- ```
1209
-
1210
- ---
1211
-
1212
- ## The Event-Driven Checklist
1213
-
1214
- Before implementing event-driven patterns:
1215
-
1216
- - [ ] Business justification exists (not just "it's modern")
1217
- - [ ] Team understands eventual consistency trade-offs
1218
- - [ ] Message broker selected and provisioned
1219
- - [ ] Event schema defined with versioning strategy
1220
- - [ ] All consumers are idempotent
1221
- - [ ] Dead letter queue configured with monitoring
1222
- - [ ] Distributed tracing in place (OpenTelemetry)
1223
- - [ ] Outbox pattern used for transactional events (if needed)
1224
- - [ ] Consumer failure handling defined (retry, DLQ, alert)
1225
- - [ ] Event catalog maintained (what events exist, who produces/consumes)
1226
- ## UNIVERSAL RULE: frontend-architecture.md
1227
- Source: .agent-context/rules/frontend-architecture.md
1228
-
1229
- # Frontend Architecture & Composition Patterns
1230
-
1231
- > A complex UI is built from simple, mathematically robust functions. State is dangerous; isolate it.
1232
-
1233
- ## 1. File Structure (Feature-Driven Design)
1234
- Organize your application by feature domain, not by file type.
1235
- - **BANNED:** Monolithic directories like `/components` (with 500 files), `/hooks`, `/api`.
1236
- - **REQUIRED (Feature Sliced):**
1237
- ```
1238
- src/
1239
- features/
1240
- authentication/
1241
- api/ #(login, logout fetchers)
1242
- components/ #(LoginForm, ProfileView)
1243
- hooks/ #(useAuth, useSession)
1244
- store.ts #(Zustand slice)
1245
- types.ts #(Zod schemas)
1246
- components/ #(Global shared UI like Button, Modal)
1247
- lib/ #(Axios instance, utility wrappers)
1248
- ```
1249
-
1250
- ## 2. Separation of State and UI (Smart vs. Dumb)
1251
- - **Dumb Components (Presentational):** Receive data via `props`, emit events via callbacks (`onAction`). They do not know about the network, global context, or databases.
1252
- - **Smart Components (Containers):** Connect to global state (Redux/Zustand), fetch data (React Query), and pass it down.
1253
- - **Rule:** An intricate UI layout component should NEVER contain a `fetch` or `useQuery` call.
1254
-
1255
- ## 3. Server State vs. Client State
1256
- Modern frontend frameworks differentiate between remote and local data.
1257
- - **Server State (Async, Cached):** Data belonging to the database. MUST be managed by tools like `TanStack Query` (React Query) or `SWR`.
1258
- - **Client State (Sync, Ephemeral):** UI toggles, modal states, form drafts. Manage via `useState`, `useContext`, or `Zustand`.
1259
- - **BANNED:** Storing API responses in a global Redux/Zustand store (e.g., `dispatch(setUsers(data))`). Use React Query instead.
1260
-
1261
- ## 4. The Composition Pattern (Avoiding Prop Drilling)
1262
- If a component takes more than 5 props, or if props are passed down through 3+ intermediate components, the architecture is broken.
1263
- - **BANNED:** `<Layout user={user} theme={theme} onLogout={handleLogout} />`
1264
- - **REQUIRED:** Use React's `children` prop and composition.
1265
- ```tsx
1266
- // ✅ Clean composition
1267
- <Layout>
1268
- <Sidebar user={user} />
1269
- <Content onLogout={handleLogout} />
1270
- </Layout>
1271
- ```
1272
-
1273
- ## 5. Explicit Component Contracts (Typing)
1274
- Every component **MUST** have an explicit, exported interface for its props.
1275
- - **BANNED:** `const Button = (props: any) => ...`
1276
- - **REQUIRED:** Prefix handlers with `on` and booleans with `is/has`.
1277
- ```typescript
1278
- export interface ButtonProps {
1279
- variant: 'primary' | 'secondary';
1280
- isLoading?: boolean;
1281
- onClick: () => void;
1282
- children: React.ReactNode;
1283
- }
1284
- ```
1285
-
1286
- ## 6. Form Handling & Validation
1287
- Never write manual state bindings for complex forms.
1288
- - **Rule:** All forms MUST use a robust library (`react-hook-form` is the standard) combined with a schema validator (`Zod`).
1289
- - **BANNED:** Creating 5 `useState` variables for 5 input fields.
1290
-
1291
- ## 7. Performance & Re-renders
1292
- React is fast until you break it.
1293
- - **Rule:** Do not pass newly instantiated objects or arrow functions directly into dependency arrays (`useEffect`) or memoized components (`React.memo`) unless wrapped in `useMemo`/`useCallback`.
1294
- - **Rule:** Never execute expensive mapping/filtering inside the render path blindly without memoization.
1295
- ## UNIVERSAL RULE: git-workflow.md
1296
- Source: .agent-context/rules/git-workflow.md
1297
-
1298
- # Git Workflow — Clean History, Atomic Commits
1299
-
1300
- > Your git log is a changelog. If it reads like gibberish, your team is lost.
1301
-
1302
- ## Commit Message Format: Conventional Commits (Enforced)
1303
-
1304
- ```
1305
- <type>(<scope>): <description>
1306
-
1307
- [optional body — explain WHY, not WHAT]
1308
- [optional footer — Breaking changes, issue references]
1309
- ```
1310
-
1311
- ### Types (Strict Set)
1312
- | Type | When | Example |
1313
- |------|------|---------|
1314
- | `feat` | New feature | `feat(auth): add JWT refresh token rotation` |
1315
- | `fix` | Bug fix | `fix(payment): handle race condition in checkout` |
1316
- | `refactor` | Code restructuring (no behavior change) | `refactor(user): extract validation to separate service` |
1317
- | `docs` | Documentation only | `docs(api): add OpenAPI schema for /orders endpoint` |
1318
- | `test` | Adding/fixing tests | `test(cart): add edge case for empty cart discount` |
1319
- | `chore` | Build, CI, config, dependencies | `chore(deps): upgrade prisma to 5.x` |
1320
- | `perf` | Performance improvement | `perf(search): add index on users.email column` |
1321
- | `style` | Formatting, semicolons (no logic change) | `style: apply prettier formatting` |
1322
- | `ci` | CI/CD changes | `ci: add Node 20 to test matrix` |
1323
-
1324
- ### Rules
1325
- 1. **Type is mandatory.** No commits without a type prefix.
1326
- 2. **Scope is recommended.** Use the module/feature name.
1327
- 3. **Description is imperative mood.** "add", not "added" or "adds".
1328
- 4. **Max 72 characters** for the subject line.
1329
- 5. **Body explains WHY,** not what. The diff shows what.
1330
-
1331
- ### ❌ BANNED Commit Messages
1332
- ```
1333
- fix bug
1334
- updates
1335
- WIP
1336
- asdf
1337
- misc changes
1338
- working now
1339
- final fix
1340
- fix fix fix
1341
- ```
1342
-
1343
- ---
1344
-
1345
- ## Branching Model
1346
-
1347
- ### Main Branches
1348
- | Branch | Purpose | Merge Strategy |
1349
- |--------|---------|---------------|
1350
- | `main` | Production-ready code | Merge commit or squash |
1351
- | `develop` | Integration branch (if using GitFlow) | Merge commit |
1352
-
1353
- ### Feature Branches
1354
- ```
1355
- Pattern: <type>/<ticket-id>-<short-description>
1356
-
1357
- Examples:
1358
- feat/AUTH-123-jwt-refresh
1359
- fix/PAY-456-checkout-race-condition
1360
- refactor/USER-789-extract-validation
1361
- chore/INFRA-101-upgrade-node-20
1362
- ```
1363
-
1364
- ### Rules
1365
- 1. Branch from `main` (or `develop` if using GitFlow)
1366
- 2. Keep branches short-lived (max 2-3 days)
1367
- 3. Rebase on `main` before creating PR — don't merge main into your branch
1368
- 4. Delete branch after merge
1369
-
1370
- ---
1371
-
1372
- ## Pull Request Standards
1373
-
1374
- ### PR Size
1375
- | Size | Lines Changed | Verdict |
1376
- |------|--------------|---------|
1377
- | Small | 1-100 | ✅ Ideal — easy to review |
1378
- | Medium | 100-300 | ⚠️ Acceptable — split if possible |
1379
- | Large | 300-500 | 🔶 Needs justification |
1380
- | Massive | 500+ | ❌ MUST be split into smaller PRs |
1381
-
1382
- **Rule:** If a PR touches more than 5 files across different modules, it's doing too much. Split it.
1383
-
1384
- ### PR Description Template
1385
- ```markdown
1386
- ## What
1387
- Brief description of what this PR does.
1388
-
1389
- ## Why
1390
- Why this change is needed. Link to issue/ticket.
1391
-
1392
- ## How
1393
- High-level approach. Mention any non-obvious design decisions.
1394
-
1395
- ## Testing
1396
- - [ ] Unit tests added/updated
1397
- - [ ] Integration tests (if applicable)
1398
- - [ ] Manual testing steps
1399
-
1400
- ## Screenshots (if UI change)
1401
- Before | After
1402
- ```
1403
-
1404
- ### PR Review Rules
1405
- 1. Every PR needs at least 1 approval
1406
- 2. Author resolves all comments before merge
1407
- 3. CI must pass (lint, test, build)
1408
- 4. No `// TODO` without a linked issue
1409
- 5. No `console.log` debugging statements in production code
1410
-
1411
- ---
1412
-
1413
- ## Commit Atomicity
1414
-
1415
- ### Rule: Each Commit Must Be a Complete, Working Unit
1416
-
1417
- ```
1418
- ❌ BANNED sequence:
1419
- 1. feat(user): add user model ← compiles? maybe
1420
- 2. fix: fix import ← fixing previous commit
1421
- 3. feat(user): add user service ← compiles? probably
1422
- 4. fix: fix typo ← fixing previous commit
1423
- 5. feat(user): add user controller ← finally works together
1424
-
1425
- ✅ REQUIRED:
1426
- 1. feat(user): add user registration module
1427
- → Model, Service, Controller, Tests — all in one complete, working commit
1428
- → Or split into logical chunks that each compile and pass tests independently
1429
- ```
1430
-
1431
- **Rule:** Every commit on `main` should compile, pass lint, and pass tests. Use interactive rebase (`git rebase -i`) to squash fix-up commits before merging.
1432
-
1433
- ---
1434
-
1435
- ## .gitignore Standards
1436
-
1437
- ### MUST Ignore
1438
- ```
1439
- # Dependencies
1440
- node_modules/
1441
- vendor/
1442
- venv/
1443
- __pycache__/
1444
- .gradle/
1445
- target/
1446
-
1447
- # Environment
1448
- .env
1449
- .env.local
1450
- .env.*.local
1451
-
1452
- # IDE
1453
- .idea/
1454
- .vscode/settings.json
1455
- *.swp
1456
- *.swo
1457
- .DS_Store
1458
- Thumbs.db
1459
-
1460
- # Build output
1461
- dist/
1462
- build/
1463
- out/
1464
- *.min.js
1465
- *.min.css
1466
-
1467
- # Logs
1468
- *.log
1469
- npm-debug.log*
1470
-
1471
- # OS
1472
- .DS_Store
1473
- Thumbs.db
1474
- ```
1475
-
1476
- ### MUST Commit
1477
- ```
1478
- .env.example # Template with placeholder values
1479
- .editorconfig # Consistent formatting across IDEs
1480
- .prettierrc # Formatter config
1481
- .eslintrc.* # Linter config
1482
- tsconfig.json # TypeScript config
1483
- docker-compose.yml # Dev environment
1484
- Makefile / Taskfile # Standard commands
1485
- ```
1486
-
1487
- ---
1488
-
1489
- ## The Git Health Check
1490
-
1491
- Before pushing:
1492
- - [ ] All commits follow Conventional Commits format
1493
- - [ ] No fixup commits (squash them)
1494
- - [ ] Branch is rebased on latest main
1495
- - [ ] CI passes locally (lint, test, build)
1496
- - [ ] No secrets in any commit (check with `git log -p | grep -i "password\|secret\|key"`)
1497
- - [ ] No merge commits in feature branch (rebase instead)
1498
- ## UNIVERSAL RULE: microservices.md
1499
- Source: .agent-context/rules/microservices.md
1500
-
1501
- # Microservices — When to Split, How to Split
1502
-
1503
- > Don't start with microservices. Earn them through pain.
1504
- > A bad monolith becomes a distributed bad monolith faster than you think.
1505
-
1506
- ## The Default: Modular Monolith
1507
-
1508
- **Start with a modular monolith. Always.** Microservices are a scaling strategy, not a design philosophy.
1509
-
1510
- A well-structured modular monolith can handle most applications indefinitely. Splitting prematurely creates distributed complexity without distributed benefits.
1511
-
1512
- ---
1513
-
1514
- ## The Split Decision Framework
1515
-
1516
- ### Prerequisites (ALL Must Be True)
1517
-
1518
- Before even considering microservices, these must exist:
1519
-
1520
- 1. **Mature CI/CD pipeline** — automated testing, deployment, rollback
1521
- 2. **Observability in place** — distributed tracing, centralized logging, metrics
1522
- 3. **Team maturity** — team understands distributed systems failure modes
1523
- 4. **Clear module boundaries** — modules already communicate through interfaces, not internals
1524
-
1525
- If any prerequisite is missing, **stop.** Fix the monolith first.
1526
-
1527
- ### Trigger Conditions (2+ Required)
1528
-
1529
- Split a module into a service ONLY when **2 or more** of these triggers exist:
1530
-
1531
- | # | Trigger | Evidence |
1532
- |---|---------|----------|
1533
- | 1 | **Deploy conflicts** | Teams block each other on releases; merge queues exceed 24h |
1534
- | 2 | **Scale mismatch** | One module needs 50-100x resources of another |
1535
- | 3 | **Team ownership collision** | Multiple teams edit the same module weekly |
1536
- | 4 | **Fault isolation** | One module crashing must not kill the entire system |
1537
- | 5 | **Technology divergence** | Module genuinely requires a different runtime/language |
1538
- | 6 | **Compliance boundary** | Regulatory requirement to isolate data processing (PCI, HIPAA) |
1539
-
1540
- ```
1541
- BANNED: "Let's use microservices because Netflix does"
1542
- BANNED: "We might need to scale later"
1543
- BANNED: "It's more modern"
1544
- BANNED: "Each developer gets their own service"
1545
- ```
1546
-
1547
- ---
1548
-
1549
- ## How to Split (The Extraction Protocol)
1550
-
1551
- ### Step 1: Identify the Seam
1552
-
1553
- Find the natural boundary in your modular monolith:
1554
- ```
1555
- GOOD seams:
1556
- - Module communicates with others through a defined interface/API
1557
- - Module has its own database tables with no foreign keys to other modules
1558
- - Module can be deployed independently without breaking others
1559
-
1560
- BAD seams:
1561
- - Two modules share a database table
1562
- - Extracting requires duplicating business logic
1563
- - Module makes 10+ synchronous calls to other modules per request
1564
- ```
1565
-
1566
- ### Step 2: Strangle, Don't Rewrite
1567
-
1568
- ```
1569
- ❌ BANNED: "Let's rewrite everything as microservices"
1570
-
1571
- ✅ REQUIRED: The Strangler Fig Pattern
1572
- 1. Build the new service alongside the monolith
1573
- 2. Route traffic to the new service gradually (feature flags, proxy rules)
1574
- 3. Verify the new service handles all cases correctly
1575
- 4. Remove the old code from the monolith
1576
- 5. Repeat for the next service, ONE AT A TIME
1577
- ```
1578
-
1579
- ### Step 3: Define the Contract
1580
-
1581
- Before extracting, define the communication contract:
1582
-
1583
- ```
1584
- REQUIRED for every service boundary:
1585
- - API contract (OpenAPI 3.1, Protobuf, or AsyncAPI for events)
1586
- - Versioning strategy (URL versioning, header versioning)
1587
- - Error contract (standardized error response format)
1588
- - SLA definition (latency, availability, throughput)
1589
- - Ownership (which team owns this service)
1590
- ```
1591
-
1592
- ---
1593
-
1594
- ## Communication Patterns
1595
-
1596
- ### Synchronous (Request-Response)
1597
-
1598
- | Pattern | When | Watch Out |
1599
- |---------|------|-----------|
1600
- | REST/HTTP | Standard CRUD operations | Cascading failures, tight coupling |
1601
- | gRPC | High-performance, internal services | Schema evolution, debugging complexity |
1602
-
1603
- **Rules:**
1604
- - Always set timeouts (connection: 1s, request: 5s default)
1605
- - Implement circuit breakers (fail-fast after N failures)
1606
- - Never chain more than 3 synchronous calls
1607
- - Implement retries with exponential backoff (transient errors only)
1608
-
1609
- ### Asynchronous (Events)
1610
-
1611
- | Pattern | When | Watch Out |
1612
- |---------|------|-----------|
1613
- | Pub/Sub | One event, multiple consumers | Message ordering, at-least-once delivery |
1614
- | Command Queue | Exactly-once processing needed | Dead letter queues, poison messages |
1615
- | Event Sourcing | Full audit trail required | Complexity, event schema evolution |
1616
-
1617
- **Rules:**
1618
- - Prefer async communication over sync between services
1619
- - Events are facts (past tense): `OrderPlaced`, `PaymentProcessed`
1620
- - Commands are requests (imperative): `ProcessPayment`, `ShipOrder`
1621
- - Always handle duplicate messages (idempotency)
1622
-
1623
- ---
1624
-
1625
- ## Data Ownership
1626
-
1627
- ### The Database-Per-Service Rule
1628
-
1629
- ```
1630
- ❌ DEATH PENALTY: Shared databases between services
1631
- Service A → Database ← Service B
1632
- // One schema change breaks both services
1633
-
1634
- ✅ REQUIRED: Each service owns its data
1635
- Service A → Database A
1636
- Service B → Database B
1637
- // Services communicate through APIs or events
1638
- ```
1639
-
1640
- ### Data Consistency
1641
-
1642
- - **Accept eventual consistency** — strong consistency across services is extremely expensive
1643
- - **Use the Saga pattern** for distributed transactions (choreography or orchestration)
1644
- - **Never use distributed 2PC (two-phase commit)** in production — it doesn't scale
1645
-
1646
- ---
1647
-
1648
- ## Anti-Patterns (Instant Rejection)
1649
-
1650
- | Anti-Pattern | Why It's Dangerous |
1651
- |-------------|-------------------|
1652
- | **Distributed Monolith** | Services that must be deployed together defeat the purpose |
1653
- | **Nano-services** | One function per service creates operational nightmare |
1654
- | **Shared libraries with logic** | Tight coupling through shared code |
1655
- | **Synchronous chains** | A→B→C→D means one failure kills everything |
1656
- | **Shared database** | Schema changes break multiple services |
1657
- | **"We'll figure out observability later"** | You won't find bugs without tracing. Ever. |
1658
-
1659
- ---
1660
-
1661
- ## The Microservices Readiness Checklist
1662
-
1663
- Before splitting ANY module:
1664
-
1665
- - [ ] 2+ trigger conditions met (documented with evidence)
1666
- - [ ] All prerequisites in place (CI/CD, observability, team maturity)
1667
- - [ ] Service boundary defined with clear ownership
1668
- - [ ] API contract defined (OpenAPI, Protobuf, AsyncAPI)
1669
- - [ ] Data ownership clear — no shared tables
1670
- - [ ] Communication pattern chosen (sync vs async)
1671
- - [ ] Failure handling designed (timeouts, circuit breakers, retries)
1672
- - [ ] Monitoring and alerting planned
1673
- - [ ] Rollback strategy defined
1674
- - [ ] Team agrees this is the right decision (not just the architect)
1675
- ## UNIVERSAL RULE: naming-conv.md
1676
- Source: .agent-context/rules/naming-conv.md
1677
-
1678
- # Naming Conventions — The "No Lazy Names" Standard
1679
-
1680
- > If your variable name needs a comment to explain it, the name is wrong.
1681
-
1682
- ## Universal Rules (All Languages)
1683
-
1684
- ### Variables: Nouns That Tell a Story
1685
- ```
1686
- ❌ BANNED ✅ REQUIRED
1687
- ─────────────────────────────────────────────
1688
- d durationInSeconds
1689
- data userProfilePayload
1690
- res httpResponse
1691
- temp unsortedItems
1692
- val discountPercentage
1693
- info orderSummary
1694
- item cartLineItem
1695
- list activeSubscriptions
1696
- obj paymentConfiguration
1697
- ```
1698
-
1699
- **Rule:** A variable name must answer "WHAT is this?" without reading surrounding code.
1700
-
1701
- ### Functions: Verbs That Declare Intent
1702
- ```
1703
- ❌ BANNED ✅ REQUIRED
1704
- ─────────────────────────────────────────────
1705
- process() validatePaymentDetails()
1706
- handle() routeIncomingWebhook()
1707
- doStuff() calculateShippingCost()
1708
- run() executeScheduledCleanup()
1709
- getData() fetchActiveUsersByRegion()
1710
- check() isEligibleForDiscount()
1711
- ```
1712
-
1713
- **Rule:** A function name must answer "WHAT does this do?" as a verb phrase. Generic verbs like `process`, `handle`, `manage` are BANNED unless suffixed with a specific noun (e.g., `handlePaymentFailure`).
1714
-
1715
- ### Booleans: is/has/can/should Prefix
1716
- ```
1717
- ❌ BANNED ✅ REQUIRED
1718
- ─────────────────────────────────────────────
1719
- active isActive
1720
- logged isLoggedIn
1721
- admin hasAdminRole
1722
- edit canEditDocument
1723
- visible shouldRenderSidebar
1724
- ```
1725
-
1726
- **Rule:** Boolean variables MUST use `is`, `has`, `can`, or `should` prefix. No exceptions.
1727
-
1728
- ### Constants: SCREAMING_SNAKE for True Constants
1729
- ```
1730
- ❌ BANNED ✅ REQUIRED
1731
- ─────────────────────────────────────────────
1732
- maxRetries = 3 MAX_RETRY_COUNT = 3
1733
- timeout = 5000 REQUEST_TIMEOUT_MS = 5000
1734
- apiUrl = "..." API_BASE_URL = "..."
1735
- ```
1736
-
1737
- **Rule:** Use SCREAMING_SNAKE_CASE for compile-time constants and config values. Include the unit in the name when applicable (`_MS`, `_BYTES`, `_COUNT`).
1738
-
1739
- ### Single-Letter Variables
1740
- **BANNED.** With exactly ONE exception:
1741
-
1742
- ```
1743
- // ALLOWED: Classic loop counter in simple iterations
1744
- for (let i = 0; i < items.length; i++) { ... }
1745
-
1746
- // BANNED: Everything else
1747
- const x = getPrice(); // ❌ What is x?
1748
- const n = users.length; // ❌ Use userCount
1749
- arr.map(v => v.id); // ❌ Use user => user.id
1750
- ```
1751
-
1752
- ---
1753
-
1754
- ## File & Directory Naming
1755
-
1756
- ### Files
1757
- | Type | Convention | Example |
1758
- |------|-----------|---------|
1759
- | Component (React/Vue) | PascalCase | `PaymentForm.tsx` |
1760
- | Module/Service | camelCase or kebab-case | `paymentService.ts` or `payment-service.ts` |
1761
- | Utility | camelCase or kebab-case | `formatCurrency.ts` or `format-currency.ts` |
1762
- | Test | Same as source + `.test`/`.spec` | `paymentService.test.ts` |
1763
- | Type/Interface | PascalCase | `PaymentTypes.ts` |
1764
- | Constant file | SCREAMING_SNAKE or kebab-case | `constants.ts` or `api-endpoints.ts` |
1765
- | Config | kebab-case | `database-config.ts` |
1766
-
1767
- **Rule:** Pick ONE convention per project and enforce it everywhere. Mixing `camelCase` and `kebab-case` in the same project is a code smell.
1768
-
1769
- ### Directories
1770
- - Always `kebab-case`: `user-management/`, `payment-processing/`
1771
- - Never PascalCase for directories (except component folders in React convention)
1772
- - No abbreviations: `auth/` → `authentication/` (unless universally understood like `api/`, `db/`)
1773
-
1774
- ---
1775
-
1776
- ## Abbreviation Policy
1777
-
1778
- ### Allowed (Universal)
1779
- `id`, `url`, `api`, `db`, `http`, `io`, `ui`, `dto`, `config`, `env`, `auth`, `admin`, `src`, `lib`, `pkg`, `cmd`, `ctx`, `err`, `req`, `res` (in HTTP handler context only)
1780
-
1781
- ### Banned
1782
- Everything else. Spell it out:
1783
- ```
1784
- ❌ usr → ✅ user
1785
- ❌ cnt → ✅ count
1786
- ❌ mgr → ✅ manager
1787
- ❌ btn → ✅ button
1788
- ❌ msg → ✅ message
1789
- ❌ impl → ✅ implementation
1790
- ❌ calc → ✅ calculate
1791
- ❌ proc → ✅ process
1792
- ```
1793
-
1794
- ---
1795
-
1796
- ## The Naming Decision Tree
1797
-
1798
- ```
1799
- Is it a boolean?
1800
- → Yes → Add is/has/can/should prefix
1801
- → No ↓
1802
-
1803
- Is it a function?
1804
- → Yes → Start with a verb (fetch, create, validate, calculate, is, has)
1805
- → No ↓
1806
-
1807
- Is it a constant?
1808
- → Yes → SCREAMING_SNAKE_CASE with unit suffix
1809
- → No ↓
1810
-
1811
- Is it a class/type/interface?
1812
- → Yes → PascalCase, noun, no prefix (not IUser, not UserInterface)
1813
- → No ↓
1814
-
1815
- It's a variable
1816
- → Descriptive noun, camelCase
1817
- → Must be understandable without context
1818
- ```
1819
- ## UNIVERSAL RULE: performance.md
1820
- Source: .agent-context/rules/performance.md
1821
-
1822
- # Performance — Measure Before You Optimize
1823
-
1824
- > Premature optimization is the root of all evil.
1825
- > But ignoring obvious performance traps is just incompetence.
1826
-
1827
- ## The Performance Prime Directive
1828
-
1829
- **Do NOT optimize without evidence.** CPU time is cheap. Developer time is expensive.
1830
-
1831
- BUT — there are patterns so obviously bad that they don't need benchmarks to reject.
1832
- Those are listed below as **Death Penalties**.
1833
-
1834
- ---
1835
-
1836
- ## Death Penalties (Always Reject)
1837
-
1838
- ### 1. N+1 Queries
1839
- ```
1840
- ❌ INSTANT REJECTION:
1841
- const users = await db.query('SELECT * FROM users');
1842
- for (const user of users) {
1843
- user.orders = await db.query('SELECT * FROM orders WHERE user_id = ?', [user.id]);
1844
- // This runs 1 + N queries. For 1000 users = 1001 database round trips.
1845
- }
1846
-
1847
- ✅ REQUIRED:
1848
- const users = await db.query('SELECT * FROM users');
1849
- const userIds = users.map(u => u.id);
1850
- const orders = await db.query('SELECT * FROM orders WHERE user_id = ANY($1)', [userIds]);
1851
- // 2 queries total, regardless of user count
1852
- // Or use ORM eager loading / DataLoader pattern
1853
- ```
1854
-
1855
- **Rule:** If you see a query inside a loop, it is almost certainly an N+1 bug. Fix it with JOINs, batch queries, or DataLoader.
1856
-
1857
- ### 2. Unbounded Queries
1858
- ```
1859
- ❌ INSTANT REJECTION:
1860
- const allUsers = await db.query('SELECT * FROM users');
1861
- // In production, "users" table has 2 million rows. Enjoy your OOM.
1862
-
1863
- ✅ REQUIRED:
1864
- const users = await db.query('SELECT * FROM users LIMIT $1 OFFSET $2', [pageSize, offset]);
1865
- // Or use cursor-based pagination for large datasets
1866
- ```
1867
-
1868
- **Rule:** EVERY query that returns a list MUST have a LIMIT. APIs MUST support pagination. Default page size: 20-50.
1869
-
1870
- ### 3. SELECT * in Production
1871
- ```
1872
- ❌ BANNED:
1873
- SELECT * FROM users; -- Fetches 30 columns when you need 3
1874
-
1875
- ✅ REQUIRED:
1876
- SELECT id, email, display_name FROM users;
1877
- ```
1878
-
1879
- **Rule:** Select only the columns you need. `SELECT *` wastes bandwidth, memory, and makes schema changes dangerous.
1880
-
1881
- ### 4. Synchronous I/O in Async Context
1882
- ```
1883
- ❌ BANNED:
1884
- // In an async Node.js server
1885
- const data = fs.readFileSync('/path/to/file');
1886
- const result = execSync('some-command');
1887
-
1888
- ✅ REQUIRED:
1889
- const data = await fs.promises.readFile('/path/to/file');
1890
- const result = await exec('some-command');
1891
- ```
1892
-
1893
- **Rule:** In async environments (Node.js, Python asyncio), NEVER block the event loop with synchronous I/O.
1894
-
1895
- ---
1896
-
1897
- ## Optimize Only With Evidence
1898
-
1899
- For everything else, follow this protocol:
1900
-
1901
- ### Step 1: Measure
1902
- ```
1903
- // Use profiling tools, not guesses
1904
- console.time('operation');
1905
- await heavyOperation();
1906
- console.timeEnd('operation');
1907
-
1908
- // Use proper APM: Datadog, New Relic, OpenTelemetry
1909
- ```
1910
-
1911
- ### Step 2: Identify the Bottleneck
1912
- - Is it CPU? Memory? I/O? Network?
1913
- - Don't optimize CPU when the bottleneck is a slow database query
1914
-
1915
- ### Step 3: Optimize the Bottleneck (Not Everything Else)
1916
- - Fix the slowest thing first
1917
- - Re-measure after each change
1918
- - Stop when performance meets requirements
1919
-
1920
- ---
1921
-
1922
- ## Caching Rules
1923
-
1924
- ### When to Cache
1925
- - Data that is read frequently, written rarely
1926
- - Expensive computations that produce the same result for same inputs
1927
- - External API responses (respect their cache headers)
1928
-
1929
- ### When NOT to Cache
1930
- - Data that changes every request
1931
- - Small, fast queries (cache overhead > query time)
1932
- - When you can't define a clear invalidation strategy
1933
-
1934
- ### The Caching Devil's Triangle
1935
- You can have 2 of 3:
1936
- 1. **Fresh data** (always up-to-date)
1937
- 2. **Fast reads** (sub-millisecond)
1938
- 3. **Simple code** (no cache layer)
1939
-
1940
- Pick your 2 and document the trade-off.
1941
-
1942
- ### Invalidation Strategy (MANDATORY)
1943
- ```
1944
- ❌ BANNED:
1945
- cache.set('users', data); // When does this expire? Who invalidates it? Nobody knows.
1946
-
1947
- ✅ REQUIRED:
1948
- cache.set('users:active', data, { ttl: 300 }); // 5 min TTL, explicit key
1949
- // AND document: "Invalidated on user.created, user.deleted, user.deactivated events"
1950
- ```
1951
-
1952
- **Rule:** NEVER add a cache without documenting the invalidation strategy. "We'll figure it out later" means "we'll have stale data in production."
1953
-
1954
- ---
1955
-
1956
- ## Connection Management
1957
-
1958
- 1. **Use connection pools** — never open/close connections per request
1959
- 2. **Set pool limits** — max connections based on infrastructure, not infinity
1960
- 3. **Implement timeouts** — connection timeout, query timeout, request timeout
1961
- 4. **Handle pool exhaustion** — fail fast with clear error, don't queue forever
1962
-
1963
- ---
1964
-
1965
- ## Frontend Performance (When Applicable)
1966
-
1967
- 1. **Lazy load** routes and heavy components
1968
- 2. **Debounce** search inputs and scroll handlers (300ms minimum)
1969
- 3. **Virtualize** long lists (don't render 10,000 DOM nodes)
1970
- 4. **Optimize images** — WebP/AVIF, responsive sizes, lazy loading
1971
- 5. **Bundle analysis** — if your JS bundle exceeds 200KB gzipped, investigate
1972
-
1973
- ---
1974
-
1975
- ## The Performance Decision Framework
1976
-
1977
- ```
1978
- Is it a known Death Penalty pattern (N+1, unbounded, SELECT *, sync I/O)?
1979
- → Yes → Fix it now, no measurement needed
1980
- → No ↓
1981
-
1982
- Is there a measurable performance problem?
1983
- → No → Don't optimize. Ship it.
1984
- → Yes ↓
1985
-
1986
- Have you identified the specific bottleneck?
1987
- → No → Profile first
1988
- → Yes → Optimize ONLY that bottleneck, re-measure, stop when sufficient
1989
- ```
1990
- ## UNIVERSAL RULE: realtime.md
1991
- Source: .agent-context/rules/realtime.md
1992
-
1993
- # Real-time & WebSockets Architecture
1994
-
1995
- > WebSockets are stateful tcp connections in a stateless HTTP world. Treat them with extreme caution.
1996
-
1997
- ## 1. Connection Management & Scaling
1998
- WebSockets hold a persistent connection, consuming file descriptors and memory on the server.
1999
- - **Rule:** NEVER scale WebSockets using a standard load balancer without sticky sessions (unless utilizing an external Pub/Sub service).
2000
- - **Architecture:** Use a dedicated external Pub/Sub layer to broadcast messages across multiple WebSocket server instances.
2001
- - **Standard:** Redis Pub/Sub (Socket.io-redis, any standard Redis adapter).
2002
- - **Enterprise:** NATS, Apache Kafka, or managed services like AWS API Gateway WebSockets / Pusher / Socket.io / Soketi.
2003
- - **Goal:** Any user can connect to Server A, and receive a message published by a background job running on Server B.
2004
-
2005
- ## 2. Authentication Context
2006
- WebSockets cannot rely on traditional HTTP Authorization headers during the handshake in browser environments (as the WebSocket API does not allow setting custom headers).
2007
- - **Rule:** Authenticate connections explicitly.
2008
- - **Method A (Cookies):** If the application uses HTTPOnly cookies, authentication happens naturally during the initial HTTP upgrade request.
2009
- - **Method B (Tickets/Tokens):** Pass the JWT or an ephemeral ticket in the connection URL query string (`?token=xyz`) or immediately after connecting via a dedicated `auth` JSON payload.
2010
- - **BANNED:** Never trust a client simply because they know the WebSocket URL.
2011
-
2012
- ## 3. The "No Business Logic in Sockets" Rule
2013
- A WebSocket controller should be treated exactly like an HTTP controller.
2014
- - **Rule:** The WebSocket handler's **only** responsibility is parsing the incoming message, validating the payload schema, and routing it to a Service/Application layer orchestrator.
2015
- - **BANNED:** Directly writing to databases, executing complex business logic, or calling external APIs directly inside the WebSocket event callback.
2016
-
2017
- ## 4. Heartbeats & Dead Connections
2018
- TCP connections drop silently (e.g., when a user drives through a tunnel).
2019
- - **Rule:** Implement Ping/Pong heartbeats. The server MUST periodically ping the client. If the client fails to respond within the expected window (e.g., 30s), the server MUST forcefully sever the connection to free resources (`ws.terminate()`, not `ws.close()`).
2020
-
2021
- ## 5. Event Design & Typing
2022
- Treat WebSocket events like a strongly typed REST API.
2023
- - **Rule:** Every WebSocket message MUST have a mandatory `type` or `event` field, and a strongly typed `payload` field defined by a schema (e.g., Zod, JSON Schema).
2024
- - **BANNED:** Emitting arbitrary strings or untyped JSON objects like `{ user_id: 1, message: "hi" }`.
2025
- - **REQUIRED:**
2026
- ```json
2027
- {
2028
- "event": "message.created",
2029
- "payload": {
2030
- "id": "msg_123",
2031
- "text": "Hello World",
2032
- "timestamp": "2026-03-01T12:00:00Z"
2033
- }
2034
- }
2035
- ```
2036
-
2037
- ## 6. Rate Limiting & Abuse Prevention
2038
- WebSockets are heavily susceptible to DoS attacks because an open connection bypasses traditional WAF rate limiters.
2039
- - **Rule:** You MUST implement message rate limiting at the application logic layer (e.g., maximum 5 messages per second per connection/user). Violators should be instantly disconnected and IP-banned temporarily.
2040
- ## UNIVERSAL RULE: security.md
2041
- Source: .agent-context/rules/security.md
2042
-
2043
- # Security — Trust Nothing, Validate Everything
2044
-
2045
- > Every user is a potential attacker. Every input is a potential exploit.
2046
- > You are not paranoid — you are professional.
2047
-
2048
- ## The Zero Trust Input Rule
2049
-
2050
- **ALL data crossing a system boundary is untrusted until validated.**
2051
-
2052
- System boundaries include:
2053
- - HTTP request bodies, query params, headers, cookies
2054
- - URL path parameters
2055
- - File uploads
2056
- - WebSocket messages
2057
- - Queue/event payloads
2058
- - Environment variables (at startup)
2059
- - Database query results (yes, even these — data could be corrupted)
2060
- - Third-party API responses
2061
-
2062
- ### Validation Protocol
2063
- ```
2064
- External Input → Schema Validation (Zod/Pydantic/Bean Validation) → Typed Internal Object
2065
-
2066
- NEVER:
2067
- External Input → Direct use in business logic
2068
- External Input → Direct use in database query
2069
- External Input → Direct interpolation into strings
2070
- ```
2071
-
2072
- ---
2073
-
2074
- ## Injection Prevention
2075
-
2076
- ### SQL Injection — Parameterized Queries Only
2077
- ```
2078
- ❌ DEATH PENALTY:
2079
- const query = `SELECT * FROM users WHERE id = ${userId}`;
2080
- const query = "SELECT * FROM users WHERE name = '" + name + "'";
2081
-
2082
- ✅ ALWAYS:
2083
- const user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);
2084
- const user = await prisma.user.findUnique({ where: { id: userId } });
2085
- ```
2086
-
2087
- **Rule:** NEVER concatenate or interpolate user input into SQL strings. Use parameterized queries or ORMs. No exceptions. Not even "just for testing."
2088
-
2089
- ### Command Injection
2090
- ```
2091
- ❌ DEATH PENALTY:
2092
- exec(`convert ${filename} output.png`);
2093
-
2094
- ✅ ALWAYS:
2095
- execFile('convert', [filename, 'output.png']);
2096
- ```
2097
-
2098
- **Rule:** Never pass user input to shell commands via string interpolation. Use argument arrays.
2099
-
2100
- ### XSS Prevention
2101
- ```
2102
- ❌ BANNED:
2103
- element.innerHTML = userInput;
2104
- dangerouslySetInnerHTML={{ __html: userInput }};
2105
-
2106
- ✅ REQUIRED:
2107
- element.textContent = userInput;
2108
- // Or sanitize with DOMPurify if HTML is absolutely needed
2109
- ```
2110
-
2111
- **Rule:** Default to text content. Only use HTML injection with explicit sanitization AND a code comment explaining WHY.
2112
-
2113
- ---
2114
-
2115
- ## Secret Management
2116
-
2117
- ### Rule: ZERO Secrets in Code
2118
-
2119
- ```
2120
- ❌ INSTANT REJECTION:
2121
- const API_KEY = "sk-abc123def456";
2122
- const DB_PASSWORD = "supersecret";
2123
- const JWT_SECRET = "my-jwt-secret";
2124
- // Even in .env.example with real values
2125
-
2126
- ✅ REQUIRED:
2127
- const API_KEY = process.env.API_KEY; // Read from environment
2128
- const DB_URL = process.env.DATABASE_URL; // Injected at runtime
2129
- ```
2130
-
2131
- ### .env Files
2132
- - `.env` → NEVER committed (must be in `.gitignore`)
2133
- - `.env.example` → Committed with placeholder values ONLY (`API_KEY=your-api-key-here`)
2134
- - `.env.local` → NEVER committed
2135
- - `.env.test` → May be committed with TEST-ONLY non-secret values
2136
-
2137
- ### Secret Rotation
2138
- If a secret is accidentally committed:
2139
- 1. Rotate the secret IMMEDIATELY (not after the PR is merged — NOW)
2140
- 2. Remove from git history (`git filter-branch` or BFG)
2141
- 3. Add to `.gitignore`
2142
- 4. Document the incident
2143
-
2144
- ---
2145
-
2146
- ## Authentication & Authorization
2147
-
2148
- ### Authentication Rules
2149
- 1. Never implement custom auth crypto — use established libraries (argon2, bcrypt, Passport, NextAuth)
2150
- 2. Hash passwords with **argon2id** (OWASP primary recommendation) — bcrypt only for legacy systems
2151
- - Argon2id: minimum 19 MiB memory, 2 iterations, 1 parallelism
2152
- - bcrypt: minimum cost 12 (legacy systems only — limited to 72 bytes, no memory-hardness)
2153
- - NEVER: MD5, SHA1, plain SHA256, or PBKDF2 without FIPS requirement
2154
- 3. Use constant-time comparison for tokens and hashes
2155
- 4. Implement rate limiting on auth endpoints (max 5 attempts per minute per IP)
2156
- 5. Session tokens must be cryptographically random (≥ 256 bits)
2157
-
2158
- ### Authorization Rules
2159
- 1. **Default deny** — if no rule grants access, deny
2160
- 2. **Server-side only** — NEVER trust client-side role checks for security
2161
- 3. Check authorization at the service layer, not just the controller
2162
- 4. Log all authorization failures with context (userId, resource, action)
2163
-
2164
- ```
2165
- ❌ BANNED: Client-side only authorization
2166
- if (user.role === 'admin') { showDeleteButton(); }
2167
- // Attacker just changes user.role in devtools
2168
-
2169
- ✅ REQUIRED: Server-side enforcement
2170
- // Controller checks auth, service enforces business rules
2171
- async deleteUser(requesterId: string, targetUserId: string) {
2172
- const requester = await this.userRepo.findById(requesterId);
2173
- if (!requester || requester.role !== Role.ADMIN) {
2174
- throw new ForbiddenError('Insufficient permissions');
2175
- }
2176
- // ... proceed with deletion
2177
- }
2178
- ```
2179
-
2180
- ---
2181
-
2182
- ## HTTP Security Headers
2183
-
2184
- Every web application MUST include:
2185
- ```
2186
- Strict-Transport-Security: max-age=31536000; includeSubDomains
2187
- Content-Security-Policy: default-src 'self'; script-src 'self'
2188
- X-Content-Type-Options: nosniff
2189
- X-Frame-Options: DENY
2190
- Referrer-Policy: strict-origin-when-cross-origin
2191
- Permissions-Policy: camera=(), microphone=(), geolocation=()
2192
- ```
2193
-
2194
- ### CORS
2195
- - NEVER use `Access-Control-Allow-Origin: *` in production
2196
- - Whitelist specific origins
2197
- - Be explicit about allowed methods and headers
2198
-
2199
- ---
2200
-
2201
- ## File Upload Security
2202
-
2203
- 1. Validate MIME type server-side (not just file extension)
2204
- 2. Set maximum file size limits
2205
- 3. Generate random filenames — never use user-provided filenames
2206
- 4. Store uploads outside the web root
2207
- 5. Scan for malware if accepting documents
2208
-
2209
- ---
2210
-
2211
- ## Supply Chain Security (OWASP 2025 A03)
2212
-
2213
- 1. **Pin all dependency versions** — use lockfiles, never `*` ranges in production
2214
- 2. **Audit dependencies regularly** — `npm audit`, `pip audit`, `composer audit`
2215
- 3. **Verify package integrity** — check checksums, use signed packages where available
2216
- 4. **Minimize dependency trees** — fewer transitive dependencies = smaller attack surface
2217
- 5. **Monitor for CVEs** — automate vulnerability scanning in CI (Dependabot, Snyk, Trivy)
2218
- 6. **Review new dependencies** — check maintainer history, download trends, and bus factor before adding
2219
-
2220
- ---
2221
-
2222
- ## .gitignore Enforcement (Mandatory)
2223
-
2224
- **If the user's INTENT is to create a new project, push to GitHub, or initialize source control, you MUST generate or verify a `.gitignore` file exists.**
2225
-
2226
- ### Minimum Required Entries
2227
- ```gitignore
2228
- # ── Secrets & Environment ──
2229
- .env
2230
- .env.local
2231
- .env.*.local
2232
- .env.production
2233
- .env.staging
2234
-
2235
- # ── Dependencies ──
2236
- node_modules/
2237
- vendor/
2238
- venv/
2239
- .venv/
2240
- __pycache__/
2241
- .gradle/
2242
- target/
2243
- bin/ # Go binaries
2244
- pkg/
2245
-
2246
- # ── Build Output ──
2247
- dist/
2248
- build/
2249
- out/
2250
- *.min.js
2251
- *.min.css
2252
- .next/
2253
- .nuxt/
2254
- .output/
2255
-
2256
- # ── IDE & Editor ──
2257
- .idea/
2258
- .vscode/settings.json
2259
- .vscode/launch.json
2260
- *.swp
2261
- *.swo
2262
- *~
2263
-
2264
- # ── OS Artifacts ──
2265
- .DS_Store
2266
- Thumbs.db
2267
- Desktop.ini
2268
- *.lnk
2269
-
2270
- # ── Logs ──
2271
- *.log
2272
- npm-debug.log*
2273
- yarn-debug.log*
2274
- pnpm-debug.log*
2275
-
2276
- # ── Testing & Coverage ──
2277
- coverage/
2278
- .nyc_output/
2279
- *.lcov
2280
-
2281
- # ── Runtime & Backup Data ──
2282
- *.pid
2283
- *.seed
2284
- *.pid.lock
2285
- .agentic-backup/
2286
-
2287
- # ── Secrets & Keys ──
2288
- *.pem
2289
- *.key
2290
- *.p12
2291
- *.jks
2292
- *.keystore
2293
- ```
2294
-
2295
- ### Rules
2296
- 1. **NEVER commit `.env`** — only `.env.example` with placeholder values
2297
- 2. **Check for leaks before push** — `git diff --cached --name-only | grep -E '\.(env|pem|key)$'` should return empty
2298
- 3. **If the project has NO `.gitignore`**, create one immediately before any `git add`
2299
- 4. **Extend per-stack** — add language-specific patterns (e.g., `__pycache__/` for Python, `target/` for Java/Rust, `.gradle/` for Kotlin)
2300
- 5. **Reference**: See `.agent-context/rules/git-workflow.md` for the full `.gitignore Standards` section
2301
-
2302
- ### MUST Commit (Whitelist)
2303
- ```
2304
- .env.example # Template with placeholder values ONLY
2305
- .editorconfig # Consistent formatting across IDEs
2306
- .gitignore # This file itself
2307
- docker-compose.yml # Dev environment definition
2308
- Makefile / Taskfile # Standard dev commands
2309
- ```
2310
-
2311
- ---
2312
-
2313
- ## The Security Checklist (Quick Reference)
2314
-
2315
- Before any code is "done", verify:
2316
-
2317
- - [ ] All inputs validated at boundaries with schemas
2318
- - [ ] No string concatenation in queries/commands
2319
- - [ ] No secrets in source code
2320
- - [ ] `.gitignore` exists and covers `.env`, `node_modules/`, build output, and IDE files
2321
- - [ ] Authentication uses established libraries
2322
- - [ ] Password hashing uses argon2id (or bcrypt for legacy)
2323
- - [ ] Authorization enforced server-side
2324
- - [ ] Security headers configured
2325
- - [ ] CORS properly restricted
2326
- - [ ] Rate limiting on sensitive endpoints
2327
- - [ ] Error responses don't leak internal details
2328
- - [ ] Logging includes security events (login failures, permission denials)
2329
- - [ ] Dependencies audited for known vulnerabilities
2330
- ## UNIVERSAL RULE: testing.md
2331
- Source: .agent-context/rules/testing.md
2332
-
2333
- # Testing — Prove It Works, Don't Pray
2334
-
2335
- > "It works on my machine" is not a test strategy.
2336
- > Untested code is broken code that hasn't been caught yet.
2337
-
2338
- ## The Test Pyramid (Enforced)
2339
-
2340
- ```
2341
- ╱ E2E ╲ Few — Slow — Expensive — Fragile
2342
- ╱──────────╲ Test critical user journeys only
2343
- ╱ Integration ╲ Medium — Test module boundaries
2344
- ╱────────────────╲ Database, API contracts, service interactions
2345
- ╱ Unit Tests ╲ Many — Fast — Cheap — Stable
2346
- ╱──────────────────────╲ Test business logic in isolation
2347
- ```
2348
-
2349
- ### Ratios
2350
- - **Unit tests:** 70% — fast, isolated, test business rules
2351
- - **Integration tests:** 20% — test boundaries (DB, APIs, modules)
2352
- - **E2E tests:** 10% — test critical user flows only
2353
-
2354
- ---
2355
-
2356
- ## What to Test (And What Not To)
2357
-
2358
- ### ✅ ALWAYS Test
2359
- - Business logic and calculations
2360
- - Input validation rules
2361
- - Edge cases (empty arrays, null values, boundary numbers)
2362
- - Error handling paths (what happens when things fail)
2363
- - State transitions and workflows
2364
- - Authorization rules
2365
-
2366
- ### ❌ NEVER Test
2367
- - Framework internals (don't test that Express routes work)
2368
- - Simple getters/setters with no logic
2369
- - Third-party library behavior
2370
- - Private implementation details (test behavior, not structure)
2371
- - Database migrations (verify schema, don't test the migration tool)
2372
-
2373
- ---
2374
-
2375
- ## Test Naming Convention
2376
-
2377
- ### Pattern: `should [expected behavior] when [condition]`
2378
-
2379
- ```
2380
- ❌ BANNED:
2381
- test('test1')
2382
- test('it works')
2383
- test('calculateDiscount')
2384
-
2385
- ✅ REQUIRED:
2386
- test('should apply 20% discount when order total exceeds $100')
2387
- test('should throw ValidationError when email format is invalid')
2388
- test('should return empty array when user has no orders')
2389
- test('should deny access when user lacks admin role')
2390
- ```
2391
-
2392
- **Rule:** A reader must understand the expected behavior WITHOUT reading the test body.
2393
-
2394
- ---
2395
-
2396
- ## Test Structure: AAA Pattern
2397
-
2398
- Every test follows **Arrange → Act → Assert**:
2399
-
2400
- ```typescript
2401
- test('should calculate shipping as free when order exceeds $50', () => {
2402
- // Arrange — Set up the scenario
2403
- const order = createOrder({ items: [{ price: 60, quantity: 1 }] });
2404
-
2405
- // Act — Execute the behavior
2406
- const shippingCost = calculateShipping(order);
2407
-
2408
- // Assert — Verify the outcome
2409
- expect(shippingCost).toBe(0);
2410
- });
2411
- ```
2412
-
2413
- ### Rules
2414
- - **ONE assert concept per test** (multiple `expect` calls are fine if they test the same concept)
2415
- - **No logic in tests** (no if/else, no loops, no try/catch)
2416
- - **Tests must be independent** — no shared mutable state, no execution order dependency
2417
- - **Tests must be deterministic** — same input = same result, every time
2418
-
2419
- ---
2420
-
2421
- ## Mocking Rules
2422
-
2423
- ### Mock at Boundaries, Not Everywhere
2424
-
2425
- ```
2426
- ❌ OVER-MOCKING (testing implementation, not behavior):
2427
- test('should call repository.save exactly once', () => {
2428
- await service.createUser(userData);
2429
- expect(repository.save).toHaveBeenCalledTimes(1);
2430
- // If you refactor to call save differently, the test breaks
2431
- // even though the behavior hasn't changed
2432
- });
2433
-
2434
- ✅ CORRECT (testing behavior):
2435
- test('should persist user and return created user', () => {
2436
- const result = await service.createUser(userData);
2437
- expect(result.id).toBeDefined();
2438
- expect(result.email).toBe(userData.email);
2439
- // You can verify the user exists in the test DB if integration test
2440
- });
2441
- ```
2442
-
2443
- ### When to Mock
2444
- - External APIs (payment gateways, email services)
2445
- - Time-dependent operations (use fake timers)
2446
- - Non-deterministic operations (random, UUID)
2447
-
2448
- ### When NOT to Mock
2449
- - Your own code in the same module ← test the real integration
2450
- - Simple utility functions ← use the real thing
2451
- - Database in integration tests ← use a test database
2452
-
2453
- ---
2454
-
2455
- ## Test Data Standards
2456
-
2457
- ### Use Factories, Not Copy-Pasted Objects
2458
-
2459
- ```
2460
- ❌ BANNED:
2461
- test('should calculate total', () => {
2462
- const order = {
2463
- id: '123',
2464
- userId: '456',
2465
- items: [{ productId: '789', price: 29.99, quantity: 2, name: 'Widget' }],
2466
- status: 'pending',
2467
- createdAt: new Date(),
2468
- updatedAt: new Date(),
2469
- // ... 15 more fields copied from another test
2470
- };
2471
- });
2472
-
2473
- ✅ REQUIRED:
2474
- test('should calculate total', () => {
2475
- const order = createTestOrder({
2476
- items: [createTestItem({ price: 29.99, quantity: 2 })],
2477
- });
2478
- // Factory fills in all other fields with sensible defaults
2479
- });
2480
- ```
2481
-
2482
- ### Rules
2483
- - Create factory functions for each domain entity
2484
- - Factories provide sensible defaults — tests override only relevant fields
2485
- - Never use production data in tests
2486
- - Use descriptive, obviously-fake data (email: `test-user@example.com`, not `john@gmail.com`)
2487
-
2488
- ---
2489
-
2490
- ## Coverage Expectations
2491
-
2492
- | Layer | Min Coverage | What to Focus On |
2493
- |-------|-------------|------------------|
2494
- | Domain / Business Logic | 90%+ | All branching, edge cases, error paths |
2495
- | Application / Service | 80%+ | Orchestration flows, error handling |
2496
- | Transport / Controller | 60%+ | Input validation, error responses |
2497
- | Utilities | 90%+ | All functions and edge cases |
2498
-
2499
- **Rule:** Coverage is a floor, not a goal. 100% coverage with bad tests is worse than 80% coverage with good tests. Focus on testing **behavior and edge cases**, not hitting a number.
2500
-
2501
- ---
2502
-
2503
- ## When to Skip Tests (Rare)
2504
-
2505
- You may skip tests ONLY for:
2506
- - Prototype/spike code (must be labeled `// SPIKE: will be replaced`)
2507
- - Pure UI layout (visual testing is better here — use Storybook/Chromatic)
2508
- - Generated code (test the generator, not the output)
2509
-
2510
- Everything else gets tested. No excuses.
2511
- ## STACK PROFILE: typescript.md
19
+ ## BOOTSTRAP CHAIN (MANDATORY)
20
+ Load every layer before responding. Do not skip steps:
21
+ 1. .agent-context/rules/
22
+ 2. .agent-context/stacks/
23
+ 3. .agent-context/blueprints/
24
+ 4. .agent-context/skills/
25
+ 5. .agent-context/prompts/
26
+ 6. .agent-context/profiles/
27
+ 7. .agent-context/state/
28
+ 8. .agent-context/policies/llm-judge-threshold.json
29
+
30
+ Primary entrypoint: .cursorrules
31
+ Mirror entrypoint: .windsurfrules
32
+ Canonical baseline: .instructions.md
33
+ ## LAYER 1: UNIVERSAL RULES (MANDATORY)
34
+ Read every file under .agent-context/rules/ before implementation:
35
+ 1. .agent-context/rules/api-docs.md
36
+ 2. .agent-context/rules/architecture.md
37
+ 3. .agent-context/rules/database-design.md
38
+ 4. .agent-context/rules/efficiency-vs-hype.md
39
+ 5. .agent-context/rules/error-handling.md
40
+ 6. .agent-context/rules/event-driven.md
41
+ 7. .agent-context/rules/frontend-architecture.md
42
+ 8. .agent-context/rules/git-workflow.md
43
+ 9. .agent-context/rules/microservices.md
44
+ 10. .agent-context/rules/naming-conv.md
45
+ 11. .agent-context/rules/performance.md
46
+ 12. .agent-context/rules/realtime.md
47
+ 13. .agent-context/rules/security.md
48
+ 14. .agent-context/rules/testing.md
49
+
50
+ Conflict resolution: prioritize data safety and API contract integrity first, then writing polish.
51
+ ## LAYER 2: STACK PROFILE (typescript.md)
2512
52
  Source: .agent-context/stacks/typescript.md
2513
-
2514
- # TypeScript Stack Profile The "Galak" Standard
2515
-
2516
- > TypeScript is not JavaScript with optional types.
2517
- > It is a contract system. Use it like one.
2518
-
2519
- ## Compiler Configuration (Non-Negotiable)
2520
-
2521
- ### tsconfig.json Strict Settings
2522
- ```json
2523
- {
2524
- "compilerOptions": {
2525
- "strict": true,
2526
- "noUncheckedIndexedAccess": true,
2527
- "noImplicitReturns": true,
2528
- "noFallthroughCasesInSwitch": true,
2529
- "noUnusedLocals": true,
2530
- "noUnusedParameters": true,
2531
- "exactOptionalPropertyTypes": true,
2532
- "forceConsistentCasingInFileNames": true,
2533
- "isolatedModules": true,
2534
- "esModuleInterop": true,
2535
- "resolveJsonModule": true,
2536
- "moduleResolution": "bundler",
2537
- "module": "ESNext",
2538
- "target": "ES2022",
2539
- "skipLibCheck": true,
2540
- "paths": {
2541
- "@/*": ["./src/*"]
2542
- }
2543
- }
2544
- }
2545
- ```
2546
-
2547
- **Rule:** `"strict": true` is mandatory. If a project has `"strict": false`, fix it before writing any new code.
2548
-
2549
- ---
2550
-
2551
- ## The `any` Ban (Zero Tolerance)
2552
-
2553
- ### Rule: `any` is BANNED. No exceptions.
2554
-
2555
- ```typescript
2556
- // ❌ INSTANT REJECTION
2557
- function processData(data: any) { ... }
2558
- const result = response.json() as any;
2559
- // @ts-ignore
2560
- // @ts-expect-error — only allowed with a comment explaining WHY and a linked issue
2561
-
2562
- // ✅ REQUIRED: Use `unknown` with type narrowing
2563
- function processData(data: unknown) {
2564
- const parsed = DataSchema.parse(data); // Zod validates and narrows
2565
- // `parsed` is now fully typed
2566
- }
2567
-
2568
- // ✅ REQUIRED: Use generics when the type varies
2569
- function getFirstItem<T>(items: T[]): T | undefined {
2570
- return items[0];
2571
- }
2572
- ```
2573
-
2574
- ### What to Use Instead of `any`
2575
- | Situation | Instead of `any`, Use |
2576
- |-----------|----------------------|
2577
- | Unknown external data | `unknown` + Zod parsing |
2578
- | Generic container | `T` (generic type parameter) |
2579
- | Object with unknown keys | `Record<string, unknown>` |
2580
- | Function argument you'll refine | `unknown` + type guard |
2581
- | Library with bad types | Write a `.d.ts` override or `unknown` wrapper |
2582
- | Event handlers | The specific event type (`MouseEvent`, `ChangeEvent<HTMLInputElement>`) |
2583
-
2584
- ---
2585
-
2586
- ## Zod at Boundaries (Mandatory)
2587
-
2588
- ### Rule: ALL External Data MUST Pass Through Zod
2589
-
2590
- ```typescript
2591
- // ❌ BANNED: Trusting external data
2592
- app.post('/users', async (req, res) => {
2593
- const { name, email, age } = req.body; // Could be anything!
2594
- await userService.create({ name, email, age });
2595
- });
2596
-
2597
- // ✅ REQUIRED: Validate with Zod at the boundary
2598
- import { z } from 'zod';
2599
-
2600
- const CreateUserSchema = z.object({
2601
- name: z.string().min(1).max(100).trim(),
2602
- email: z.string().email().toLowerCase(),
2603
- age: z.number().int().min(13).max(150),
2604
- });
2605
-
2606
- type CreateUserDto = z.infer<typeof CreateUserSchema>;
2607
-
2608
- app.post('/users', async (req, res) => {
2609
- const parsed = CreateUserSchema.safeParse(req.body);
2610
- if (!parsed.success) {
2611
- return res.status(400).json({ errors: parsed.error.flatten() });
2612
- }
2613
- // `parsed.data` is fully typed and validated
2614
- await userService.create(parsed.data);
2615
- });
2616
- ```
2617
-
2618
- ### Where Zod is MANDATORY
2619
- - API request bodies (POST, PUT, PATCH)
2620
- - Query parameters and path parameters
2621
- - WebSocket incoming messages
2622
- - Queue/event payloads from external systems
2623
- - Environment variables at startup
2624
- - Third-party API responses (trust but verify)
2625
- - File upload metadata
2626
-
2627
- ### Zod Best Practices
2628
- ```typescript
2629
- // ✅ Reuse schemas — define once, use everywhere
2630
- // src/modules/user/user.schema.ts
2631
- export const UserSchema = z.object({
2632
- id: z.string().uuid(),
2633
- email: z.string().email(),
2634
- name: z.string().min(1).max(100),
2635
- role: z.enum(['user', 'admin', 'moderator']),
2636
- createdAt: z.coerce.date(),
2637
- });
2638
-
2639
- export const CreateUserSchema = UserSchema.omit({ id: true, createdAt: true });
2640
- export const UpdateUserSchema = CreateUserSchema.partial();
2641
-
2642
- // Types derived from schemas — single source of truth
2643
- export type User = z.infer<typeof UserSchema>;
2644
- export type CreateUserDto = z.infer<typeof CreateUserSchema>;
2645
- export type UpdateUserDto = z.infer<typeof UpdateUserSchema>;
2646
- ```
2647
-
2648
- ---
2649
-
2650
- ## API Documentation (Mandatory)
2651
-
2652
- ### Rule: No API Endpoint Exists Without Documentation
2653
-
2654
- Every API endpoint MUST have corresponding documentation. When creating or modifying an endpoint, you MUST simultaneously update the API documentation.
2655
-
2656
- ### Preferred Approach: OpenAPI (Swagger)
2657
-
2658
- ```typescript
2659
- // Option 1: Code-first with decorators (NestJS)
2660
- @ApiOperation({ summary: 'Create a new user' })
2661
- @ApiBody({ type: CreateUserDto })
2662
- @ApiResponse({ status: 201, description: 'User created', type: UserResponseDto })
2663
- @ApiResponse({ status: 400, description: 'Validation error' })
2664
- @ApiResponse({ status: 409, description: 'Email already exists' })
2665
- @Post()
2666
- async createUser(@Body() dto: CreateUserDto): Promise<UserResponseDto> { ... }
2667
-
2668
- // Option 2: Schema-first with Zod + zod-to-openapi
2669
- import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
2670
- extendZodWithOpenApi(z);
2671
-
2672
- const CreateUserSchema = z.object({
2673
- name: z.string().min(1).openapi({ example: 'John Doe' }),
2674
- email: z.string().email().openapi({ example: 'john@example.com' }),
2675
- }).openapi('CreateUserRequest');
2676
- ```
2677
-
2678
- ### Documentation Checklist (Per Endpoint)
2679
- - [ ] HTTP method and path
2680
- - [ ] Request body schema (with examples)
2681
- - [ ] Query/path parameter descriptions
2682
- - [ ] All possible response codes with schemas
2683
- - [ ] Authentication requirements
2684
- - [ ] Rate limiting information (if applicable)
2685
-
2686
- ### Documentation Must Stay in Sync
2687
- ```
2688
- ❌ BANNED:
2689
- // Endpoint accepts `role` field but docs don't mention it
2690
- // Endpoint returns 422 but docs only show 400
2691
-
2692
- ✅ REQUIRED:
2693
- // When you change an endpoint → update the schema/docs in the SAME commit
2694
- // Use code-first tools so docs are generated from the actual types
2695
- ```
2696
-
2697
- ---
2698
-
2699
- ## Import Style
2700
-
2701
- ### Path Aliases (Required)
2702
- ```typescript
2703
- // ❌ BANNED: Deep relative imports
2704
- import { UserService } from '../../../modules/user/user.service';
2705
- import { AppError } from '../../../../shared/errors/app-error';
2706
-
2707
- // ✅ REQUIRED: Path aliases
2708
- import { UserService } from '@/modules/user/user.service';
2709
- import { AppError } from '@/shared/errors/app-error';
2710
- ```
2711
-
2712
- ### Import Order (Enforced by ESLint)
2713
- ```typescript
2714
- // 1. Node built-ins
2715
- import { readFile } from 'node:fs/promises';
2716
- import { join } from 'node:path';
2717
-
2718
- // 2. External packages
2719
- import { z } from 'zod';
2720
- import { PrismaClient } from '@prisma/client';
2721
-
2722
- // 3. Internal modules (path aliases)
2723
- import { UserService } from '@/modules/user/user.service';
2724
- import { AppError } from '@/shared/errors/app-error';
2725
-
2726
- // 4. Relative imports (same module only)
2727
- import { CreateUserDto } from './user.dto';
2728
- import { UserRepository } from './user.repository';
2729
- ```
2730
-
2731
- ---
2732
-
2733
- ## Async Patterns
2734
-
2735
- ### Rule: Prefer async/await Over Raw Promises
2736
- ```typescript
2737
- // ❌ BANNED: Promise chain hell
2738
- function getUser(id: string) {
2739
- return userRepo.findById(id)
2740
- .then(user => {
2741
- if (!user) throw new NotFoundError('User', id);
2742
- return orderRepo.findByUserId(user.id);
2743
- })
2744
- .then(orders => ({ ...user, orders }))
2745
- .catch(err => { throw err; });
2746
- }
2747
-
2748
- // ✅ REQUIRED: Clean async/await
2749
- async function getUser(id: string): Promise<UserWithOrders> {
2750
- const user = await userRepo.findById(id);
2751
- if (!user) throw new NotFoundError('User', id);
2752
-
2753
- const orders = await orderRepo.findByUserId(user.id);
2754
- return { ...user, orders };
2755
- }
2756
- ```
2757
-
2758
- ### Parallel Execution
2759
- ```typescript
2760
- // ❌ SLOW: Sequential when operations are independent
2761
- const user = await getUser(id);
2762
- const orders = await getOrders(id);
2763
- const preferences = await getPreferences(id);
2764
-
2765
- // ✅ FAST: Parallel independent operations
2766
- const [user, orders, preferences] = await Promise.all([
2767
- getUser(id),
2768
- getOrders(id),
2769
- getPreferences(id),
2770
- ]);
2771
- ```
2772
-
2773
- ---
2774
-
2775
- ## Enum and Union Types
2776
-
2777
- ### Prefer `as const` Unions Over Enums
2778
- ```typescript
2779
- // ⚠️ ACCEPTABLE but verbose:
2780
- enum OrderStatus {
2781
- PENDING = 'pending',
2782
- CONFIRMED = 'confirmed',
2783
- SHIPPED = 'shipped',
2784
- DELIVERED = 'delivered',
2785
- }
2786
-
2787
- // ✅ PREFERRED: const assertion + union type
2788
- const ORDER_STATUSES = ['pending', 'confirmed', 'shipped', 'delivered'] as const;
2789
- type OrderStatus = (typeof ORDER_STATUSES)[number];
2790
- // OrderStatus = 'pending' | 'confirmed' | 'shipped' | 'delivered'
2791
-
2792
- // Works perfectly with Zod:
2793
- const OrderStatusSchema = z.enum(ORDER_STATUSES);
2794
- ```
2795
-
2796
- ---
2797
-
2798
- ## Preferred Libraries (V1.0 — 2025)
2799
-
2800
- | Need | Library | Why |
2801
- |------|---------|-----|
2802
- | Runtime | Bun / Node 20+ | ESM native, fast, batteries-included |
2803
- | Validation | `zod` | 0 deps, type inference, composable |
2804
- | ORM | `prisma` or `drizzle-orm` | Type-safe queries, migration support |
2805
- | HTTP framework | `hono` / `fastify` / Next.js | Lightweight, modern, tree-shakeable |
2806
- | Testing | `vitest` | Vite-native, Jest-compatible API, fast |
2807
- | Linting | `eslint` + `@typescript-eslint` | Community standard |
2808
- | Formatting | `prettier` | Community standard |
2809
- | Date | `date-fns` or `Temporal` (when stable) | Tree-shakeable, immutable |
2810
- | HTTP client | `ky` / `ofetch` / built-in `fetch` | Lightweight, modern |
2811
- | Password | `bcrypt` / `argon2` | Proven, secure |
2812
- | Logger | `pino` | Fastest JSON logger for Node.js |
2813
- | Env | `@t3-oss/env-core` + Zod | Type-safe env validation |
2814
-
2815
- ---
2816
-
2817
- ## Banned Patterns
2818
-
2819
- | Pattern | Why | Alternative |
2820
- |---------|-----|-------------|
2821
- | `any` type | Defeats TypeScript's purpose | `unknown` + narrowing |
2822
- | `// @ts-ignore` | Hides real type errors | Fix the type or `@ts-expect-error` with comment |
2823
- | `var` keyword | Function scoping bugs | `const` (default) or `let` |
2824
- | `==` loose equality | Type coercion surprises | `===` always |
2825
- | `console.log` in production | Not structured, not configurable | Use `pino` or structured logger |
2826
- | `new Date()` without timezone | Timezone bugs | Explicit UTC or use date-fns |
2827
- | Default exports | Naming inconsistency across imports | Named exports only |
2828
- | Barrel re-exports (`index.ts`) | Circular dependency magnets | Direct imports or module public API only |
2829
- | `moment.js` | Deprecated, massive bundle | `date-fns` or `dayjs` |
2830
- | `class` with inheritance (deep chains) | Fragile hierarchies | Composition + interfaces |
2831
- ## BLUEPRINT PROFILE: api-nextjs.md
53
+ Summary: TypeScript Stack Profile — The "Galak" Standard
54
+ Load this stack profile to enforce language-specific conventions.
55
+ ## LAYER 3: BLUEPRINT PROFILE (api-nextjs.md)
2832
56
  Source: .agent-context/blueprints/api-nextjs.md
2833
-
2834
- # Blueprint: Next.js API Project (App Router)
2835
-
2836
- > This blueprint defines how to scaffold a Next.js API project from scratch.
2837
- > Follow this structure exactly. Do not deviate.
2838
-
2839
- ## Tech Stack
2840
- - **Runtime:** Node.js 20+ / Bun
2841
- - **Framework:** Next.js 14+ (App Router)
2842
- - **Validation:** Zod
2843
- - **ORM:** Prisma (or Drizzle)
2844
- - **Auth:** NextAuth.js v5 / Lucia Auth
2845
- - **Testing:** Vitest
2846
-
2847
- ## Project Structure
2848
-
2849
- ```
2850
- project-name/
2851
- ├── src/
2852
- │ ├── app/ # Next.js App Router
2853
- │ │ ├── api/ # API routes
2854
- │ │ │ ├── auth/
2855
- │ │ │ │ └── [...nextauth]/
2856
- │ │ │ │ └── route.ts # Auth handler
2857
- │ │ │ ├── users/
2858
- │ │ │ │ ├── route.ts # GET /api/users, POST /api/users
2859
- │ │ │ │ └── [id]/
2860
- │ │ │ │ └── route.ts # GET/PUT/DELETE /api/users/:id
2861
- │ │ │ └── health/
2862
- │ │ │ └── route.ts # GET /api/health
2863
- │ │ ├── layout.tsx # Root layout
2864
- │ │ └── page.tsx # Root page
2865
- │ │
2866
- │ ├── modules/ # Feature modules (domain-driven)
2867
- │ │ ├── user/
2868
- │ │ │ ├── user.service.ts # Business logic
2869
- │ │ │ ├── user.repository.ts # Data access
2870
- │ │ │ ├── user.schema.ts # Zod schemas + DTOs
2871
- │ │ │ ├── user.types.ts # Type definitions
2872
- │ │ │ └── __tests__/
2873
- │ │ │ └── user.service.test.ts
2874
- │ │ └── order/
2875
- │ │ ├── order.service.ts
2876
- │ │ ├── order.repository.ts
2877
- │ │ ├── order.schema.ts
2878
- │ │ └── order.types.ts
2879
- │ │
2880
- │ ├── shared/ # Cross-cutting concerns
2881
- │ │ ├── config/
2882
- │ │ │ └── env.ts # Zod-validated environment variables
2883
- │ │ ├── errors/
2884
- │ │ │ ├── app-error.ts # Base error class
2885
- │ │ │ └── error-handler.ts # Global error handler for API routes
2886
- │ │ ├── middleware/
2887
- │ │ │ ├── auth.ts # Auth middleware
2888
- │ │ │ └── validate.ts # Request validation middleware
2889
- │ │ ├── lib/
2890
- │ │ │ ├── prisma.ts # Prisma client singleton
2891
- │ │ │ └── logger.ts # Pino logger setup
2892
- │ │ └── types/
2893
- │ │ └── api.ts # Shared API types (ApiResponse, etc.)
2894
- │ │
2895
- │ └── components/ # UI components (if full-stack)
2896
- │ ├── ui/ # Primitives
2897
- │ └── layout/ # Layout components
2898
-
2899
- ├── prisma/
2900
- │ ├── schema.prisma # Database schema
2901
- │ └── migrations/ # Database migrations
2902
-
2903
- ├── public/ # Static assets
2904
- ├── .env.example # Environment template
2905
- ├── .eslintrc.json # ESLint config
2906
- ├── .prettierrc # Prettier config
2907
- ├── next.config.mjs # Next.js config
2908
- ├── tsconfig.json # TypeScript config (strict!)
2909
- ├── vitest.config.ts # Vitest config
2910
- └── package.json
2911
- ```
2912
-
2913
- ## API Route Handler Pattern
2914
-
2915
- ```typescript
2916
- // src/app/api/users/route.ts
2917
- import { NextRequest, NextResponse } from 'next/server';
2918
- import { CreateUserSchema } from '@/modules/user/user.schema';
2919
- import { userService } from '@/modules/user/user.service';
2920
- import { handleApiError } from '@/shared/errors/error-handler';
2921
-
2922
- export async function GET(request: NextRequest) {
2923
- try {
2924
- const searchParams = request.nextUrl.searchParams;
2925
- const page = Number(searchParams.get('page') ?? '1');
2926
- const limit = Number(searchParams.get('limit') ?? '20');
2927
-
2928
- const users = await userService.findAll({ page, limit });
2929
- return NextResponse.json({ data: users });
2930
- } catch (error) {
2931
- return handleApiError(error);
2932
- }
2933
- }
2934
-
2935
- export async function POST(request: NextRequest) {
2936
- try {
2937
- const body = await request.json();
2938
- const parsed = CreateUserSchema.safeParse(body);
2939
-
2940
- if (!parsed.success) {
2941
- return NextResponse.json(
2942
- { error: { code: 'VALIDATION_ERROR', details: parsed.error.flatten() } },
2943
- { status: 400 },
2944
- );
2945
- }
2946
-
2947
- const user = await userService.create(parsed.data);
2948
- return NextResponse.json({ data: user }, { status: 201 });
2949
- } catch (error) {
2950
- return handleApiError(error);
2951
- }
2952
- }
2953
- ```
2954
-
2955
- ## Environment Validation Pattern
2956
-
2957
- ```typescript
2958
- // src/shared/config/env.ts
2959
- import { z } from 'zod';
2960
-
2961
- const envSchema = z.object({
2962
- NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
2963
- DATABASE_URL: z.string().url(),
2964
- NEXTAUTH_SECRET: z.string().min(32),
2965
- NEXTAUTH_URL: z.string().url(),
2966
- PORT: z.coerce.number().default(3000),
2967
- });
2968
-
2969
- export const env = envSchema.parse(process.env);
2970
- export type Env = z.infer<typeof envSchema>;
2971
- ```
2972
-
2973
- ## Error Handler Pattern
2974
-
2975
- ```typescript
2976
- // src/shared/errors/error-handler.ts
2977
- import { NextResponse } from 'next/server';
2978
- import { AppError } from './app-error';
2979
- import { logger } from '@/shared/lib/logger';
2980
-
2981
- export function handleApiError(error: unknown): NextResponse {
2982
- if (error instanceof AppError) {
2983
- logger.warn('Application error', {
2984
- code: error.code,
2985
- message: error.message,
2986
- context: error.context,
2987
- });
2988
-
2989
- return NextResponse.json(
2990
- { error: { code: error.code, message: error.message } },
2991
- { status: error.statusCode },
2992
- );
2993
- }
2994
-
2995
- logger.error('Unhandled error', {
2996
- error: error instanceof Error ? error.message : String(error),
2997
- stack: error instanceof Error ? error.stack : undefined,
2998
- });
2999
-
3000
- return NextResponse.json(
3001
- { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },
3002
- { status: 500 },
3003
- );
3004
- }
3005
- ```
3006
-
3007
- ## Scaffolding Checklist
3008
-
3009
- When using this blueprint, verify:
3010
- - [ ] `tsconfig.json` has `"strict": true` and all recommended flags
3011
- - [ ] `.env.example` exists with ALL required variables (placeholder values only)
3012
- - [ ] Prisma client is a singleton (not re-created per request)
3013
- - [ ] All API routes use Zod validation at the boundary
3014
- - [ ] Error handler is used in every route (no unhandled errors)
3015
- - [ ] Logger is configured (not `console.log`)
3016
- - [ ] Path aliases (`@/`) are configured in tsconfig AND next.config
3017
- - [ ] API documentation is generated or maintained alongside routes
3018
- ## CI/CD GUARDRAILS: ci-github-actions.md
3019
- Source: .agent-context/blueprints/ci-github-actions.md
3020
-
3021
- # Blueprint: GitHub Actions CI/CD Pipeline
3022
-
3023
- > Automate everything. Trust nothing. Ship with confidence.
3024
-
3025
- ## Tech Stack
3026
-
3027
- | Layer | Tool |
3028
- |-------|------|
3029
- | CI/CD | GitHub Actions |
3030
- | Runners | GitHub-hosted (ubuntu-latest) |
3031
- | Caching | actions/cache, setup-node cache |
3032
- | Security | OIDC for cloud auth, pinned action SHAs |
3033
- | Artifacts | actions/upload-artifact |
3034
-
3035
- ## Pipeline Architecture
3036
-
3037
- ```
3038
- ┌─────────────────────────────────────────────────────────┐
3039
- │ PR / Push Trigger │
3040
- ├──────────┬──────────┬──────────┬──────────┬─────────────┬─────────────┤
3041
- │ Lint │ Build │ Test │ Security │ Docs │ LLM Judge │
3042
- │ (ESLint/ │ (tsc / │ (Vitest/ │ (Audit / │ (OpenAPI │ (Checklist │
3043
- │ ruff) │ build) │ pytest) │ Trivy) │ validate) │ enforcement)│
3044
- ├──────────┴──────────┴──────────┴──────────┴─────────────┴─────────────┤
3045
- │ Gate: All jobs must pass │
3046
- ├─────────────────────────────────────────────────────────┤
3047
- │ Deploy (on main only) │
3048
- │ Staging → Smoke Tests → Production (manual approval) │
3049
- └─────────────────────────────────────────────────────────┘
3050
- ```
3051
-
3052
- ## Required Workflows
3053
-
3054
- ### 1. CI Workflow (`ci.yml`)
3055
-
3056
- Runs on every PR and push to main.
3057
-
3058
- ```yaml
3059
- name: CI
3060
-
3061
- on:
3062
- pull_request:
3063
- branches: [main]
3064
- push:
3065
- branches: [main]
3066
-
3067
- permissions:
3068
- contents: read
3069
-
3070
- concurrency:
3071
- group: ci-${{ github.ref }}
3072
- cancel-in-progress: true
3073
-
3074
- jobs:
3075
- lint:
3076
- runs-on: ubuntu-latest
3077
- timeout-minutes: 10
3078
- steps:
3079
- - uses: actions/checkout@<pin-sha>
3080
- - uses: actions/setup-node@<pin-sha>
3081
- with:
3082
- node-version-file: '.nvmrc'
3083
- cache: 'npm'
3084
- - run: npm ci
3085
- - run: npm run lint
3086
- - run: npm run type-check
3087
-
3088
- test:
3089
- runs-on: ubuntu-latest
3090
- timeout-minutes: 15
3091
- steps:
3092
- - uses: actions/checkout@<pin-sha>
3093
- - uses: actions/setup-node@<pin-sha>
3094
- with:
3095
- node-version-file: '.nvmrc'
3096
- cache: 'npm'
3097
- - run: npm ci
3098
- - run: npm run test -- --coverage
3099
- - uses: actions/upload-artifact@<pin-sha>
3100
- with:
3101
- name: coverage
3102
- path: coverage/
3103
-
3104
- security:
3105
- runs-on: ubuntu-latest
3106
- timeout-minutes: 10
3107
- steps:
3108
- - uses: actions/checkout@<pin-sha>
3109
- - run: npm audit --audit-level=high
3110
- # Add Trivy, CodeQL, or Snyk as needed
3111
-
3112
- build:
3113
- needs: [lint, test, security]
3114
- runs-on: ubuntu-latest
3115
- timeout-minutes: 15
3116
- steps:
3117
- - uses: actions/checkout@<pin-sha>
3118
- - uses: actions/setup-node@<pin-sha>
3119
- with:
3120
- node-version-file: '.nvmrc'
3121
- cache: 'npm'
3122
- - run: npm ci
3123
- - run: npm run build
3124
- - uses: actions/upload-artifact@<pin-sha>
3125
- with:
3126
- name: build
3127
- path: dist/
3128
-
3129
- llm-judge:
3130
- needs: [lint, test, security, build]
3131
- runs-on: ubuntu-latest
3132
- timeout-minutes: 12
3133
- env:
3134
- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
3135
- ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
3136
- GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
3137
- GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
3138
- GITHUB_HEAD_SHA: ${{ github.sha }}
3139
- steps:
3140
- - uses: actions/checkout@<pin-sha>
3141
- with:
3142
- fetch-depth: 0 # Full history required for accurate git diff
3143
- - uses: actions/setup-node@<pin-sha>
3144
- with:
3145
- node-version: '22'
3146
- - name: Run LLM Judge
3147
- run: node scripts/llm-judge.mjs
3148
- - name: Upload LLM Judge machine report
3149
- if: always()
3150
- uses: actions/upload-artifact@<pin-sha>
3151
- with:
3152
- name: llm-judge-report
3153
- path: .agent-context/state/llm-judge-report.json
3154
- ```
3155
-
3156
- ### 2. Deploy Workflow (`deploy.yml`)
3157
-
3158
- Runs on push to main (after CI passes).
3159
-
3160
- ```yaml
3161
- name: Deploy
3162
-
3163
- on:
3164
- workflow_run:
3165
- workflows: [CI]
3166
- types: [completed]
3167
- branches: [main]
3168
-
3169
- permissions:
3170
- id-token: write # OIDC
3171
- contents: read
3172
-
3173
- jobs:
3174
- deploy-staging:
3175
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
3176
- runs-on: ubuntu-latest
3177
- environment: staging
3178
- steps:
3179
- # Authenticate via OIDC — no static secrets
3180
- # Deploy to staging
3181
- # Run smoke tests against staging
3182
-
3183
- deploy-production:
3184
- needs: deploy-staging
3185
- runs-on: ubuntu-latest
3186
- environment:
3187
- name: production
3188
- url: https://your-app.com
3189
- steps:
3190
- # Deploy to production
3191
- # Run health checks
3192
- # Notify team (Slack, Discord)
3193
- ```
3194
-
3195
- ## Security Rules
3196
-
3197
- 1. **Pin actions to commit SHA** — not `@v3`, not `@latest`
3198
- 2. **Use OIDC** for cloud provider auth — delete static credentials
3199
- 3. **Minimal permissions** — set `permissions:` at workflow level, least privilege
3200
- 4. **Never print secrets** — GitHub masks them in logs, but don't `echo` them
3201
- 5. **Restrict self-hosted runners** — ephemeral containers only, never persistent VMs
3202
- 6. **Require PR approval** for workflows from forks
3203
- 7. **Minimize prompt scope** — send diff + checklist only, never full secret-bearing config
3204
-
3205
- ## LLM Judge Annotation Contract
3206
-
3207
- The judge emits a machine-friendly payload line and artifact that can be consumed by annotation scripts:
3208
-
3209
- - Log line: `JSON_REPORT: { ... }`
3210
- - Artifact file: `.agent-context/state/llm-judge-report.json`
3211
- - Normalized severity values: `critical`, `high`, `medium`, `low`
3212
- - Override artifact path (optional): `LLM_JUDGE_OUTPUT_PATH`
3213
-
3214
- ## Efficiency Rules
3215
-
3216
- 1. **Cache dependencies** — `actions/cache` or setup-action built-in cache
3217
- 2. **Use concurrency** — cancel previous runs on the same branch
3218
- 3. **Set timeouts** — prevent stuck jobs from burning minutes
3219
- 4. **Matrix builds** for multi-platform/version testing
3220
- 5. **Reusable workflows** — centralize common pipelines in a `.github` repo
3221
- 6. **Skip unnecessary jobs** — use path filters for targeted CI
3222
-
3223
- ```yaml
3224
- # Example: Only run frontend tests when frontend code changes
3225
- on:
3226
- push:
3227
- paths:
3228
- - 'src/frontend/**'
3229
- - 'package.json'
3230
- ```
3231
-
3232
- ## Scaffolding Checklist
3233
-
3234
- When setting up GitHub Actions for a new project:
3235
-
3236
- - [ ] Create `.github/workflows/ci.yml` with lint, test, build, security
3237
- - [ ] Create `.github/workflows/deploy.yml` with staging + production
3238
- - [ ] Pin ALL third-party actions to commit SHA
3239
- - [ ] Set `permissions: contents: read` at workflow level
3240
- - [ ] Configure `concurrency` to cancel in-progress runs
3241
- - [ ] Add `timeout-minutes` to every job
3242
- - [ ] Set up caching for package manager
3243
- - [ ] Configure environment protection rules for production
3244
- - [ ] Create `.nvmrc` or `.tool-versions` for runtime version
3245
- - [ ] Configure branch protection: require CI pass before merge
3246
- - [ ] Add `llm-judge` job that evaluates PR against `pr-checklist.md`
3247
- ## CI/CD GUARDRAILS: ci-gitlab.md
3248
- Source: .agent-context/blueprints/ci-gitlab.md
3249
-
3250
- # Blueprint: GitLab CI/CD Pipeline
3251
-
3252
- > Same principles as GitHub Actions, adapted for GitLab's pipeline model.
3253
-
3254
- ## Tech Stack
3255
-
3256
- | Layer | Tool |
3257
- |-------|------|
3258
- | CI/CD | GitLab CI/CD |
3259
- | Config | `.gitlab-ci.yml` |
3260
- | Runners | Shared runners or dedicated (Docker executor) |
3261
- | Registry | GitLab Container Registry |
3262
- | Caching | GitLab CI cache (per-branch, per-job) |
3263
-
3264
- ## Pipeline Architecture
3265
-
3266
- ```yaml
3267
- stages:
3268
- - validate # Lint + type check
3269
- - test # Unit + integration tests
3270
- - security # Dependency audit + SAST
3271
- - build # Compile, bundle, containerize
3272
- - judge # LLM-as-a-Judge checklist gate
3273
- - deploy # Staging → Production
3274
- ```
3275
-
3276
- ## Pipeline Template (`.gitlab-ci.yml`)
3277
-
3278
- ```yaml
3279
- default:
3280
- image: node:22-alpine
3281
- cache:
3282
- key:
3283
- files: [package-lock.json]
3284
- paths: [node_modules/]
3285
- policy: pull-push
3286
-
3287
- stages:
3288
- - validate
3289
- - test
3290
- - security
3291
- - build
3292
- - judge
3293
- - deploy
3294
-
3295
- # ─── VALIDATE ───────────────────────────────────────────
3296
- lint:
3297
- stage: validate
3298
- script:
3299
- - npm ci --prefer-offline
3300
- - npm run lint
3301
- - npm run type-check
3302
- rules:
3303
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
3304
- - if: '$CI_COMMIT_BRANCH == "main"'
3305
-
3306
- # ─── TEST ───────────────────────────────────────────────
3307
- test:unit:
3308
- stage: test
3309
- script:
3310
- - npm ci --prefer-offline
3311
- - npm run test -- --coverage
3312
- coverage: '/All files\s*\|\s*(\d+\.?\d*)\s*\|/'
3313
- artifacts:
3314
- reports:
3315
- coverage_report:
3316
- coverage_format: cobertura
3317
- path: coverage/cobertura-coverage.xml
3318
- paths: [coverage/]
3319
- expire_in: 7 days
3320
-
3321
- test:integration:
3322
- stage: test
3323
- services:
3324
- - postgres:16-alpine
3325
- variables:
3326
- POSTGRES_DB: test_db
3327
- POSTGRES_USER: test_user
3328
- POSTGRES_PASSWORD: test_pass
3329
- DATABASE_URL: "postgresql://test_user:test_pass@postgres:5432/test_db"
3330
- script:
3331
- - npm ci --prefer-offline
3332
- - npm run test:integration
3333
-
3334
- # ─── SECURITY ───────────────────────────────────────────
3335
- audit:
3336
- stage: security
3337
- script:
3338
- - npm audit --audit-level=high
3339
- allow_failure: false
3340
-
3341
- sast:
3342
- stage: security
3343
- # GitLab's built-in SAST template
3344
- include:
3345
- - template: Security/SAST.gitlab-ci.yml
3346
-
3347
- # ─── BUILD ──────────────────────────────────────────────
3348
- build:
3349
- stage: build
3350
- script:
3351
- - npm ci --prefer-offline
3352
- - npm run build
3353
- artifacts:
3354
- paths: [dist/]
3355
- expire_in: 1 day
3356
- rules:
3357
- - if: '$CI_COMMIT_BRANCH == "main"'
3358
-
3359
- # ─── LLM JUDGE ──────────────────────────────────────────
3360
- llm:judge:
3361
- stage: judge
3362
- image: node:22-alpine
3363
- variables:
3364
- OPENAI_API_KEY: $OPENAI_API_KEY
3365
- ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
3366
- GEMINI_API_KEY: $GEMINI_API_KEY
3367
- # CI_MERGE_REQUEST_DIFF_BASE_SHA and CI_COMMIT_SHA are set automatically
3368
- # by GitLab for merge request pipelines — no manual configuration needed.
3369
- before_script:
3370
- - git fetch --unshallow || true # Ensure full history for git diff
3371
- script:
3372
- - node scripts/llm-judge.mjs
3373
- artifacts:
3374
- when: always
3375
- paths:
3376
- - .agent-context/state/llm-judge-report.json
3377
- expire_in: 7 days
3378
- rules:
3379
- - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
3380
-
3381
- # ─── DEPLOY ─────────────────────────────────────────────
3382
- deploy:staging:
3383
- stage: deploy
3384
- environment:
3385
- name: staging
3386
- url: https://staging.example.com
3387
- script:
3388
- - echo "Deploy to staging"
3389
- # Add your deployment commands
3390
- rules:
3391
- - if: '$CI_COMMIT_BRANCH == "main"'
3392
-
3393
- deploy:production:
3394
- stage: deploy
3395
- environment:
3396
- name: production
3397
- url: https://example.com
3398
- script:
3399
- - echo "Deploy to production"
3400
- # Add your deployment commands
3401
- rules:
3402
- - if: '$CI_COMMIT_BRANCH == "main"'
3403
- when: manual # Require manual approval
3404
- needs: [deploy:staging]
3405
- ```
3406
-
3407
- ## Key Differences from GitHub Actions
3408
-
3409
- | Concern | GitHub Actions | GitLab CI |
3410
- |---------|---------------|-----------|
3411
- | Config file | `.github/workflows/*.yml` | `.gitlab-ci.yml` (single file) |
3412
- | Jobs linkage | `needs:` | `stages:` (sequential) + `needs:` (DAG) |
3413
- | Secrets | GitHub Secrets | CI/CD Variables (masked + protected) |
3414
- | Caching | `actions/cache` | Built-in `cache:` directive |
3415
- | Services | Docker Compose / service containers | `services:` directive |
3416
- | Environments | Environment protection rules | Environment + `when: manual` |
3417
- | Includes | Reusable workflows | `include:` with `template:` |
3418
-
3419
- ## Security Rules
3420
-
3421
- 1. **Protected variables** — mark secrets as Protected + Masked
3422
- 2. **Protected branches** — only deploy from protected branches
3423
- 3. **Include GitLab SAST/DAST** templates for automated scanning
3424
- 4. **Limit runner access** — use tags to route jobs to appropriate runners
3425
- 5. **Artifact expiration** — set `expire_in` on all artifacts
3426
- 6. **Limit LLM input scope** — send only merge diff + checklist context
3427
-
3428
- ## Scaffolding Checklist
3429
-
3430
- - [ ] Create `.gitlab-ci.yml` with validate, test, security, build, deploy stages
3431
- - [ ] Configure CI/CD variables for secrets (masked + protected)
3432
- - [ ] Set up caching for package manager lockfile
3433
- - [ ] Add coverage reporting with Cobertura format
3434
- - [ ] Configure environments (staging, production) with manual approval
3435
- - [ ] Include SAST template for security scanning
3436
- - [ ] Set up merge request pipelines with `rules:`
3437
- - [ ] Configure branch protection rules
3438
- - [ ] Add `timeout` to long-running jobs
3439
- - [ ] Use `needs:` for DAG optimization where possible
3440
- - [ ] Add `llm:judge` stage that enforces `pr-checklist.md`
3441
-
3442
- ## LLM Judge Annotation Contract
3443
-
3444
- For MR annotations and dashboards, parse either:
3445
-
3446
- - Log line: `JSON_REPORT: { ... }`
3447
- - Artifact: `.agent-context/state/llm-judge-report.json`
3448
-
3449
- Severity values are normalized by the judge to: `critical`, `high`, `medium`, `low`.
57
+ Summary: Blueprint: Next.js API Project (App Router)
58
+ Load this blueprint when scaffolding or changing architecture boundaries.
59
+ ## LAYER 3B: CI/CD GUARDRAILS
60
+ Load these CI blueprints when pipeline or release logic is touched:
61
+ 1. .agent-context/blueprints/ci-github-actions.md
62
+ 2. .agent-context/blueprints/ci-gitlab.md
3450
63
  ## SKILL PACK: Frontend
3451
64
  Source: .agent-context/skills/frontend.md
3452
65
  Default tier: advance
3453
66
  Selected tier: advance
3454
67
  Evidence: Frontend usability audit, accessibility checks, and visual regression output.
3455
-
3456
- # Frontend Skill Pack
3457
-
3458
- Default tier: `advance`
3459
-
3460
- ## Purpose
3461
- Deliver frontend experiences that are visually intentional, accessible, and efficient.
3462
-
3463
- ## In Scope
3464
- - UI architecture and component composition
3465
- - Responsive layouts and breakpoints
3466
- - Motion, animation, and interaction polish
3467
- - Accessibility and keyboard flows
3468
- - Conversion clarity and onboarding flow
3469
-
3470
- ## Must-Have Checks
3471
- - Feature-driven folder structure
3472
- - Smart and dumb component separation
3473
- - Server state and client state separation
3474
- - Reduced-motion fallback
3475
- - Accessibility baseline pass
3476
- - Responsive behavior verified on mobile and desktop
3477
-
3478
- ## Evidence
3479
- - Usability audit result
3480
- - Visual regression output
3481
- - Accessibility checklist result
3482
- - Release notes for motion and interaction changes
3483
-
3484
- ## Fallback
3485
- - If a feature cannot meet the advance tier, ship standard mode only as a temporary compatibility path.
3486
-
68
+ Purpose: Unified frontend delivery covering UI architecture, motion, accessibility, and conversion clarity.
69
+ Load this skill pack and apply every Must-Have Check.
3487
70
  ## SKILL PACK: Fullstack
3488
71
  Source: .agent-context/skills/fullstack.md
3489
72
  Default tier: advance
3490
73
  Selected tier: advance
3491
74
  Evidence: End-to-end tests, contract validation, and feature parity review.
3492
-
3493
- # Fullstack Skill Pack
3494
-
3495
- Default tier: `advance`
3496
-
3497
- ## Purpose
3498
- Coordinate frontend and backend delivery as a single product system.
3499
-
3500
- ## In Scope
3501
- - Feature slicing across UI and API boundaries
3502
- - Shared validation contracts
3503
- - End-to-end flows and release readiness
3504
- - Performance, accessibility, and observability together
3505
-
3506
- ## Must-Have Checks
3507
- - Single feature directory with clear public API
3508
- - Frontend and backend contracts aligned
3509
- - End-to-end test coverage for critical paths
3510
- - Release notes explain UX and API impact together
3511
-
3512
- ## Evidence
3513
- - Feature parity checklist
3514
- - End-to-end test report
3515
- - Contract validation output
3516
- - Release artifact bundle
3517
-
3518
- ## Fallback
3519
- - Split delivery only when the feature boundary is explicit and the evidence bundle is still complete.
3520
-
75
+ Purpose: Single-path product delivery across frontend and backend boundaries.
76
+ Load this skill pack and apply every Must-Have Check.
3521
77
  ## SKILL PACK: CLI
3522
78
  Source: .agent-context/skills/cli.md
3523
79
  Default tier: advance
3524
80
  Selected tier: advance
3525
81
  Evidence: CLI smoke tests, dry-run output, and automation-friendly reports.
3526
-
3527
- # CLI Skill Pack
3528
-
3529
- Default tier: `advance`
3530
-
3531
- ## Purpose
3532
- Create smart command-line workflows that guide users efficiently and safely.
3533
-
3534
- ## In Scope
3535
- - Interactive initialization and upgrade flows
3536
- - Safe defaults and confirmation steps
3537
- - Machine-readable output for automation
3538
- - Validation and self-healing hooks
3539
- - Cross-platform shell behavior
3540
-
3541
- ## Must-Have Checks
3542
- - Explicit command help and examples
3543
- - Deterministic output format for automation
3544
- - Safe destructive-action guards
3545
- - Validation before mutation
3546
- - Exit codes reflect success and failure clearly
3547
-
3548
- ## Evidence
3549
- - CLI smoke tests
3550
- - Machine-readable report output
3551
- - Upgrade dry-run output
3552
- - Cross-platform execution notes
3553
-
3554
- ## Fallback
3555
- - Standard mode can remain available for compatibility, but advance is the default user experience.
3556
-
3557
- ## STATE MAP: architecture-map.md
3558
- Source: .agent-context/state/architecture-map.md
3559
-
3560
- # Architecture Map (State Awareness)
3561
-
3562
- > This file defines protected architectural boundaries for AI-assisted changes.
3563
-
3564
- ## Boundary Classification
3565
-
3566
- | Module/Path Pattern | Criticality | Change Policy | Required Checks |
3567
- |---------------------|-------------|---------------|-----------------|
3568
- | `src/modules/payment/**` | critical | Must preserve transactional behavior and idempotency | Unit + integration + rollback test |
3569
- | `src/modules/authentication/**` | critical | Never bypass auth guards or token validation | Security audit + integration tests |
3570
- | `src/modules/**/repository/**` | high | Preserve query contracts and avoid N+1 regressions | Query plan review + performance audit |
3571
- | `src/features/**` | medium | Keep UI contracts stable and avoid API drift | Component tests + contract checks |
3572
- | `src/shared/**` | high | Backward compatibility required for public utilities | Cross-module usage validation |
3573
-
3574
- ## Required Agent Behavior
3575
-
3576
- 1. Before editing a `critical` area, load `.agent-context/review-checklists/security-audit.md` and `.agent-context/review-checklists/performance-audit.md`.
3577
- 2. For boundary-crossing changes, verify no circular dependencies are introduced (see `dependency-map.md`).
3578
- 3. Every critical-path change must include explicit risk notes in PR description.
3579
-
3580
- ## Project-Specific Notes
3581
-
3582
- - Replace placeholder path patterns with your actual module map.
3583
- - Mark payment, identity, and financial reconciliation flows as `critical`.
3584
- - Keep this file updated whenever module ownership changes.
3585
- ## STATE MAP: dependency-map.md
3586
- Source: .agent-context/state/dependency-map.md
3587
-
3588
- # Dependency Map (State Awareness)
3589
-
3590
- > This map documents allowed dependency direction to prevent circular references during refactors.
3591
-
3592
- ## Layer Dependency Rules
3593
-
3594
- 1. Transport layer may depend on Service layer.
3595
- 2. Service layer may depend on Domain contracts and Repository interfaces.
3596
- 3. Infrastructure layer may implement Repository interfaces.
3597
- 4. Domain layer must not depend on Transport or Infrastructure.
3598
-
3599
- ## Module-Level Constraints
3600
-
3601
- | Source Module | Allowed Dependencies | Forbidden Dependencies |
3602
- |---------------|----------------------|------------------------|
3603
- | `authentication` | `shared`, `user` | `payment` internals |
3604
- | `payment` | `shared`, `billing`, `notification` contracts | `authentication` internals |
3605
- | `reporting` | `shared`, read-only repository ports | write-side service internals |
3606
- | `frontend` | public API clients only | direct repository access |
3607
-
3608
- ## Circular Dependency Guardrail
3609
-
3610
- When refactoring:
3611
-
3612
- 1. Detect import graph changes before applying bulk edits.
3613
- 2. Reject any change introducing `A -> B -> A` cycles.
3614
- 3. Move shared contracts to `shared` module when two-way dependencies appear.
3615
-
3616
- ## Project-Specific Notes
3617
-
3618
- - Replace sample modules with your real domain modules.
3619
- - Keep this map synchronized with architecture decisions and ADRs.
3620
- ## REVIEW CHECKLIST: pr-checklist.md
3621
- Source: .agent-context/review-checklists/pr-checklist.md
3622
-
3623
- # PR Checklist — The Quality Gate
3624
-
3625
- > Run this before declaring any task "done."
3626
- > If ANY item fails, the task is NOT complete.
3627
-
3628
- ## Instructions for Agent
3629
-
3630
- When asked to review code using this checklist, evaluate EVERY item below.
3631
- For each failed item, provide a Reasoning Chain (see `.cursorrules` → Reasoning Clause).
3632
- Output format:
3633
-
3634
- ```
3635
- ## PR REVIEW RESULTS
3636
- ━━━━━━━━━━━━━━━━━━━
3637
-
3638
- ✅ [Item] — Passes
3639
- ❌ [Item] — FAILS
3640
- 📌 Rule: [rule file + section]
3641
- ❌ Problem: [specific issue found]
3642
- ✅ Fix: [what to change]
3643
-
3644
- VERDICT: PASS ✅ / FAIL ❌ (X/Y items passed)
3645
- ```
3646
-
3647
- ---
3648
-
3649
- ## The Checklist
3650
-
3651
- ### 1. Naming (→ rules/naming-conv.md)
3652
- - [ ] All variables are descriptive nouns (no `data`, `temp`, `val`, `x`)
3653
- - [ ] All functions start with a verb (no `userData()`, `orderLogic()`)
3654
- - [ ] All booleans use `is/has/can/should` prefix
3655
- - [ ] Constants use SCREAMING_SNAKE_CASE with unit suffix
3656
- - [ ] No single-letter variables (except `i` in classic for-loops)
3657
- - [ ] File names follow the project's chosen convention consistently
3658
-
3659
- ### 2. Architecture (→ rules/architecture.md)
3660
- - [ ] No layer leaks (controllers don't query DB, services don't return HTTP responses)
3661
- - [ ] Feature-based file organization (not technical grouping)
3662
- - [ ] Dependencies flow inward (transport → service → repository)
3663
- - [ ] Module boundaries respected (no reaching into another module's internals)
3664
- - [ ] Domain layer has zero external dependencies
3665
-
3666
- ### 3. Type Safety (→ stacks/typescript.md)
3667
- - [ ] No `any` type anywhere (use `unknown` + narrowing)
3668
- - [ ] No `// @ts-ignore` (use `@ts-expect-error` with justification comment)
3669
- - [ ] All function return types are explicit
3670
- - [ ] Zod schemas validate ALL external input at boundaries
3671
- - [ ] Types derived from Zod schemas (single source of truth)
3672
-
3673
- ### 4. Error Handling (→ rules/error-handling.md)
3674
- - [ ] No empty catch blocks
3675
- - [ ] No `catch(e) { console.log(e) }` without re-throw or recovery
3676
- - [ ] Typed error classes used (not generic `new Error('...')`)
3677
- - [ ] Error responses don't leak internal details to clients
3678
- - [ ] Structured logging with context (traceId, userId, action)
3679
-
3680
- ### 5. Security (→ rules/security.md)
3681
- - [ ] All user input validated at boundaries (Zod/schema)
3682
- - [ ] No SQL/command string concatenation with user input
3683
- - [ ] No secrets hardcoded in source code
3684
- - [ ] Auth checked server-side (not client-side only)
3685
- - [ ] Error messages don't reveal internal system details
3686
- - [ ] CORS configured (not `*` in production)
3687
-
3688
- ### 6. Performance (→ rules/performance.md)
3689
- - [ ] No N+1 queries (no queries inside loops)
3690
- - [ ] All list queries have LIMIT/pagination
3691
- - [ ] No `SELECT *` (only needed columns)
3692
- - [ ] No synchronous I/O in async context
3693
- - [ ] Cache has documented invalidation strategy (if caching used)
3694
-
3695
- ### 7. Testing (→ rules/testing.md)
3696
- - [ ] Business logic has unit tests
3697
- - [ ] Test names follow `should [behavior] when [condition]`
3698
- - [ ] Tests follow AAA pattern (Arrange → Act → Assert)
3699
- - [ ] No implementation detail testing (test behavior, not structure)
3700
- - [ ] Test data uses factories (no copy-pasted objects)
3701
-
3702
- ### 8. Dependencies (→ rules/efficiency-vs-hype.md)
3703
- - [ ] No new dependencies added without justification
3704
- - [ ] No dependency that replaces < 20 lines of code
3705
- - [ ] New packages checked for maintenance health
3706
- - [ ] Bundle impact considered (frontend)
3707
-
3708
- ### 9. Git (→ rules/git-workflow.md)
3709
- - [ ] Commit messages follow Conventional Commits
3710
- - [ ] No `console.log` debugging statements
3711
- - [ ] No `// TODO` without a linked issue
3712
- - [ ] No commented-out code blocks
3713
- - [ ] `.env` not committed
3714
-
3715
- ### 10. Documentation
3716
- - [ ] API endpoints have OpenAPI/Swagger documentation
3717
- - [ ] Complex business logic has comments explaining WHY
3718
- - [ ] Public functions/methods have JSDoc/docstrings
3719
- - [ ] README updated if new setup steps required
82
+ Purpose: Smart command-line delivery with safe defaults, machine-readable output, and upgrade flows.
83
+ Load this skill pack and apply every Must-Have Check.
84
+ ## LAYER 7: STATE AWARENESS (MANDATORY)
85
+ Load these files before touching critical paths:
86
+ 1. .agent-context/state/architecture-map.md
87
+ 2. .agent-context/state/dependency-map.md
88
+ Use these maps to prevent unsafe cross-module changes.
89
+ ## REVIEW CHECKLISTS (MANDATORY)
90
+ 1. .agent-context/review-checklists/pr-checklist.md
91
+ 2. .agent-context/review-checklists/security-audit.md (when security-sensitive)
92
+ 3. .agent-context/review-checklists/performance-audit.md (when perf-critical)
93
+ Do not claim done before checklist pass.