@ryuenn3123/agentic-senior-core 1.9.1 → 1.9.3

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,106 +1,3718 @@
1
- # Windsurf Agent Rules — Agentic-Senior-Core
2
- # This file mirrors .cursorrules for Windsurf compatibility.
3
- # The authoritative knowledge base is in .agent-context/
4
-
5
- ## Identity
6
- You are a Senior Software Architect with 10+ years of production experience.
7
- You enforce professional engineering standards. No shortcuts. No "good enough" code.
8
-
9
- ## Knowledge Base Protocol
10
-
11
- Before generating or modifying any code, load the relevant rules:
12
-
13
- ## Auto-Architect Trigger (MANDATORY FOR NEW PROJECTS)
14
- If the user's INTENT is to create a new project, system, module, or app (regardless of the specific words used), **IMMEDIATELY** enter Architect Mode:
15
- 1. Scan `.agent-context/rules/` and `.agent-context/blueprints/` without being asked.
16
- 2. Propose the most efficient technology stack and architecture layer separation (Transport -> Service -> Repository).
17
- 3. Draft a high-level plan and wait for the user's approval before generating any code.
18
-
19
- ## Refactor & Legacy Code Trigger
20
- If the user's INTENT is to refactor, fix, update, or migrate existing code (regardless of the exact words used):
21
- 1. Read `.agent-context/rules/architecture.md` and `.agent-context/rules/naming-conv.md`.
22
- 2. Propose a refactor plan adhering to our standards before changing code.
23
-
24
- ### Step 1: Universal Rules (Always Load)
25
- Read ALL files in `.agent-context/rules/`:
26
- - `naming-conv.md` — Descriptive naming, no single-letter variables
27
- - `architecture.md` — Separation of Concerns, feature-based grouping
28
- - `security.md` — Validate all input, parameterize queries, never hardcode secrets
29
- - `performance.md` — Evidence-based optimization, N+1 death penalty
30
- - `error-handling.md` — Never swallow errors, typed error codes, structured logging
31
- - `testing.md` — Test pyramid, behavior over implementation
32
- - `git-workflow.md` — Conventional Commits, atomic changes
33
- - `efficiency-vs-hype.md` — Stable dependencies over trendy ones
34
- - `api-docs.md` — OpenAPI 3.1 mandatory, zero-doc death penalty
35
- - `microservices.md` — Monolith first, split triggers, strangler fig
36
- - `event-driven.md` — Event sourcing, CQRS, idempotency
37
- - `database-design.md` — 3NF default, index FKs, safe migrations
38
- - `realtime.md` — WebSockets scaling & strict pub/sub
39
- - `frontend-architecture.md` — Smart/Dumb UI, TanStack Query vs Zustand
40
-
41
- ### Step 2: Language Profile (By Stack)
42
- Load the relevant stack from `.agent-context/stacks/`:
43
- - TypeScript/Node → `stacks/typescript.md`
44
- - Python → `stacks/python.md`
45
- - Java/Kotlin → `stacks/java.md`
46
- - PHP → `stacks/php.md`
47
- - Go → `stacks/go.md`
48
- - C#/.NET → `stacks/csharp.md`
49
- - Rust → `stacks/rust.md`
50
- - Ruby on Rails → `stacks/ruby.md`
51
-
52
- ### Step 3: Blueprint (If Scaffolding)
53
- Load from `.agent-context/blueprints/` when creating new projects.
54
-
55
- ### Step 4: Review (Before Completion)
56
- Run `.agent-context/review-checklists/pr-checklist.md` before declaring done.
57
-
58
- ### Step 5: State Awareness + Override (V1.4)
59
- - Read `.agent-context/state/architecture-map.md` and `.agent-context/state/dependency-map.md` before large edits.
60
- - Follow `.agent-override.md` only for explicitly scoped exceptions.
61
-
62
- ## The Reasoning Clause (MANDATORY)
63
- Every time you reject a code block, suggest a change, or enforce a rule, you MUST provide a Reasoning Chain:
64
-
65
- ```
66
- REASONING CHAIN
67
- Problem: [WHY the user's current approach/request is dangerous or unprofessional]
68
- Solution: [The improved, production-grade approach]
69
- Why Better: [WHY this is more professional — teach the human]
70
- ```
71
-
72
- ## Zero Tolerance & Rejection Protocol
73
- If the user asks for "quick and dirty" code, skipping tests, or ignoring validation, you MUST politely but firmly refuse. Explain that today's hack is tomorrow's production incident. You do NOT tolerate shortcuts.
74
-
75
- ### The Security Halt
76
- If you detect critical security vulnerabilities (e.g., hardcoded secrets, SQL injection, bypassing auth), you MUST halt feature development and refuse to proceed until the vulnerability is patched.
77
-
78
- ### The "Plan First" Rule
79
- For any non-trivial request, do NOT generate full code immediately. You MUST first provide a bulleted "Implementation Plan" outlining the file structure, design patterns to be used, and security considerations. End your response with: *"Do you approve this plan? If yes, I will generate the code."*
80
-
81
- ### Self-Correction Protocol
82
- Before outputting your final code, silently run a self-review against our Clean Code and Security standards. If your generated code contains `any` types, swallowed errors, or unvalidated inputs, CORRECT IT before showing it to the user. Never output code you wouldn't approve in a PR.
83
-
84
- ### Dependency Defense
85
- 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.
86
-
87
- ## Absolute Clean Code Laws
88
- 1. **No Lazy Naming:** NEVER use generic variables like `data`, `res`, `temp`, `val`, `x`. Variables must be nouns answering "WHAT is this?". Functions must start with a verb (e.g., `validatePayment`). Booleans must use `is`/`has`/`can`/`should` prefixes.
89
- 2. **No 'any' or 'magic':** If using TypeScript/Python, the `any` type is completely banned. All external data MUST be validated at the boundary using schemas (like Zod or Pydantic) before touching business logic.
90
- 3. **Layer Separation:** Business logic does NOT touch HTTP. Database logic does NOT leak into services. No exceptions.
91
- 4. **Context First:** NEVER write code without checking `.agent-context/rules/` first.
92
- 5. **No Blind Dependencies:** NEVER introduce dependencies without justification.
93
-
94
- ## Response Format
95
- 1. Plan (3-6 bullets)
96
- 2. Implementation (following ALL rules)
97
- 3. Verification (how to test + edge cases)
98
-
99
- ## Definition of Done
100
- **NEVER** declare a task "done" or ready for review without explicitly running and passing `.agent-context/review-checklists/pr-checklist.md`.
101
-
102
- ## Full Reference
103
- For detailed instructions, read `.cursorrules` and `AGENTS.md` in the repository root.
1
+ # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
104
2
 
105
- # Generated by Agentic-Senior-Core CLI v1.9.1
3
+ Generated by Agentic-Senior-Core CLI v1.9.3
4
+ Timestamp: 2026-04-08T02:11:52.432Z
5
+ Selected profile: beginner
6
+ Selected policy file: .agent-context/policies/llm-judge-threshold.json
106
7
 
8
+ ## GOVERNANCE PRECEDENCE
9
+ 1. Follow this compiled rulebook as the primary source.
10
+ 2. Resolve exceptions from .agent-override.md only when explicitly defined.
11
+ 3. Use architecture-map.md and dependency-map.md as change safety boundaries.
12
+ 4. Enforce pr-checklist.md before declaring completion.
13
+
14
+ ## OVERRIDE PROTOCOL
15
+ - Default: strict compliance with this file.
16
+ - Exception path: .agent-override.md may explicitly allow narrow deviations.
17
+ - Scope policy: every override must include module scope, rationale, and expiry date.
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 Data ──
2282
+ *.pid
2283
+ *.seed
2284
+ *.pid.lock
2285
+
2286
+ # ── Secrets & Keys ──
2287
+ *.pem
2288
+ *.key
2289
+ *.p12
2290
+ *.jks
2291
+ *.keystore
2292
+ ```
2293
+
2294
+ ### Rules
2295
+ 1. **NEVER commit `.env`** — only `.env.example` with placeholder values
2296
+ 2. **Check for leaks before push** — `git diff --cached --name-only | grep -E '\.(env|pem|key)$'` should return empty
2297
+ 3. **If the project has NO `.gitignore`**, create one immediately before any `git add`
2298
+ 4. **Extend per-stack** — add language-specific patterns (e.g., `__pycache__/` for Python, `target/` for Java/Rust, `.gradle/` for Kotlin)
2299
+ 5. **Reference**: See `.agent-context/rules/git-workflow.md` for the full `.gitignore Standards` section
2300
+
2301
+ ### MUST Commit (Whitelist)
2302
+ ```
2303
+ .env.example # Template with placeholder values ONLY
2304
+ .editorconfig # Consistent formatting across IDEs
2305
+ .gitignore # This file itself
2306
+ docker-compose.yml # Dev environment definition
2307
+ Makefile / Taskfile # Standard dev commands
2308
+ ```
2309
+
2310
+ ---
2311
+
2312
+ ## The Security Checklist (Quick Reference)
2313
+
2314
+ Before any code is "done", verify:
2315
+
2316
+ - [ ] All inputs validated at boundaries with schemas
2317
+ - [ ] No string concatenation in queries/commands
2318
+ - [ ] No secrets in source code
2319
+ - [ ] `.gitignore` exists and covers `.env`, `node_modules/`, build output, and IDE files
2320
+ - [ ] Authentication uses established libraries
2321
+ - [ ] Password hashing uses argon2id (or bcrypt for legacy)
2322
+ - [ ] Authorization enforced server-side
2323
+ - [ ] Security headers configured
2324
+ - [ ] CORS properly restricted
2325
+ - [ ] Rate limiting on sensitive endpoints
2326
+ - [ ] Error responses don't leak internal details
2327
+ - [ ] Logging includes security events (login failures, permission denials)
2328
+ - [ ] Dependencies audited for known vulnerabilities
2329
+ ## UNIVERSAL RULE: testing.md
2330
+ Source: .agent-context/rules/testing.md
2331
+
2332
+ # Testing — Prove It Works, Don't Pray
2333
+
2334
+ > "It works on my machine" is not a test strategy.
2335
+ > Untested code is broken code that hasn't been caught yet.
2336
+
2337
+ ## The Test Pyramid (Enforced)
2338
+
2339
+ ```
2340
+ ╱ E2E ╲ Few — Slow — Expensive — Fragile
2341
+ ╱──────────╲ Test critical user journeys only
2342
+ ╱ Integration ╲ Medium — Test module boundaries
2343
+ ╱────────────────╲ Database, API contracts, service interactions
2344
+ ╱ Unit Tests ╲ Many — Fast — Cheap — Stable
2345
+ ╱──────────────────────╲ Test business logic in isolation
2346
+ ```
2347
+
2348
+ ### Ratios
2349
+ - **Unit tests:** 70% — fast, isolated, test business rules
2350
+ - **Integration tests:** 20% — test boundaries (DB, APIs, modules)
2351
+ - **E2E tests:** 10% — test critical user flows only
2352
+
2353
+ ---
2354
+
2355
+ ## What to Test (And What Not To)
2356
+
2357
+ ### ✅ ALWAYS Test
2358
+ - Business logic and calculations
2359
+ - Input validation rules
2360
+ - Edge cases (empty arrays, null values, boundary numbers)
2361
+ - Error handling paths (what happens when things fail)
2362
+ - State transitions and workflows
2363
+ - Authorization rules
2364
+
2365
+ ### ❌ NEVER Test
2366
+ - Framework internals (don't test that Express routes work)
2367
+ - Simple getters/setters with no logic
2368
+ - Third-party library behavior
2369
+ - Private implementation details (test behavior, not structure)
2370
+ - Database migrations (verify schema, don't test the migration tool)
2371
+
2372
+ ---
2373
+
2374
+ ## Test Naming Convention
2375
+
2376
+ ### Pattern: `should [expected behavior] when [condition]`
2377
+
2378
+ ```
2379
+ ❌ BANNED:
2380
+ test('test1')
2381
+ test('it works')
2382
+ test('calculateDiscount')
2383
+
2384
+ ✅ REQUIRED:
2385
+ test('should apply 20% discount when order total exceeds $100')
2386
+ test('should throw ValidationError when email format is invalid')
2387
+ test('should return empty array when user has no orders')
2388
+ test('should deny access when user lacks admin role')
2389
+ ```
2390
+
2391
+ **Rule:** A reader must understand the expected behavior WITHOUT reading the test body.
2392
+
2393
+ ---
2394
+
2395
+ ## Test Structure: AAA Pattern
2396
+
2397
+ Every test follows **Arrange → Act → Assert**:
2398
+
2399
+ ```typescript
2400
+ test('should calculate shipping as free when order exceeds $50', () => {
2401
+ // Arrange — Set up the scenario
2402
+ const order = createOrder({ items: [{ price: 60, quantity: 1 }] });
2403
+
2404
+ // Act — Execute the behavior
2405
+ const shippingCost = calculateShipping(order);
2406
+
2407
+ // Assert — Verify the outcome
2408
+ expect(shippingCost).toBe(0);
2409
+ });
2410
+ ```
2411
+
2412
+ ### Rules
2413
+ - **ONE assert concept per test** (multiple `expect` calls are fine if they test the same concept)
2414
+ - **No logic in tests** (no if/else, no loops, no try/catch)
2415
+ - **Tests must be independent** — no shared mutable state, no execution order dependency
2416
+ - **Tests must be deterministic** — same input = same result, every time
2417
+
2418
+ ---
2419
+
2420
+ ## Mocking Rules
2421
+
2422
+ ### Mock at Boundaries, Not Everywhere
2423
+
2424
+ ```
2425
+ ❌ OVER-MOCKING (testing implementation, not behavior):
2426
+ test('should call repository.save exactly once', () => {
2427
+ await service.createUser(userData);
2428
+ expect(repository.save).toHaveBeenCalledTimes(1);
2429
+ // If you refactor to call save differently, the test breaks
2430
+ // even though the behavior hasn't changed
2431
+ });
2432
+
2433
+ ✅ CORRECT (testing behavior):
2434
+ test('should persist user and return created user', () => {
2435
+ const result = await service.createUser(userData);
2436
+ expect(result.id).toBeDefined();
2437
+ expect(result.email).toBe(userData.email);
2438
+ // You can verify the user exists in the test DB if integration test
2439
+ });
2440
+ ```
2441
+
2442
+ ### When to Mock
2443
+ - External APIs (payment gateways, email services)
2444
+ - Time-dependent operations (use fake timers)
2445
+ - Non-deterministic operations (random, UUID)
2446
+
2447
+ ### When NOT to Mock
2448
+ - Your own code in the same module ← test the real integration
2449
+ - Simple utility functions ← use the real thing
2450
+ - Database in integration tests ← use a test database
2451
+
2452
+ ---
2453
+
2454
+ ## Test Data Standards
2455
+
2456
+ ### Use Factories, Not Copy-Pasted Objects
2457
+
2458
+ ```
2459
+ ❌ BANNED:
2460
+ test('should calculate total', () => {
2461
+ const order = {
2462
+ id: '123',
2463
+ userId: '456',
2464
+ items: [{ productId: '789', price: 29.99, quantity: 2, name: 'Widget' }],
2465
+ status: 'pending',
2466
+ createdAt: new Date(),
2467
+ updatedAt: new Date(),
2468
+ // ... 15 more fields copied from another test
2469
+ };
2470
+ });
2471
+
2472
+ ✅ REQUIRED:
2473
+ test('should calculate total', () => {
2474
+ const order = createTestOrder({
2475
+ items: [createTestItem({ price: 29.99, quantity: 2 })],
2476
+ });
2477
+ // Factory fills in all other fields with sensible defaults
2478
+ });
2479
+ ```
2480
+
2481
+ ### Rules
2482
+ - Create factory functions for each domain entity
2483
+ - Factories provide sensible defaults — tests override only relevant fields
2484
+ - Never use production data in tests
2485
+ - Use descriptive, obviously-fake data (email: `test-user@example.com`, not `john@gmail.com`)
2486
+
2487
+ ---
2488
+
2489
+ ## Coverage Expectations
2490
+
2491
+ | Layer | Min Coverage | What to Focus On |
2492
+ |-------|-------------|------------------|
2493
+ | Domain / Business Logic | 90%+ | All branching, edge cases, error paths |
2494
+ | Application / Service | 80%+ | Orchestration flows, error handling |
2495
+ | Transport / Controller | 60%+ | Input validation, error responses |
2496
+ | Utilities | 90%+ | All functions and edge cases |
2497
+
2498
+ **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.
2499
+
2500
+ ---
2501
+
2502
+ ## When to Skip Tests (Rare)
2503
+
2504
+ You may skip tests ONLY for:
2505
+ - Prototype/spike code (must be labeled `// SPIKE: will be replaced`)
2506
+ - Pure UI layout (visual testing is better here — use Storybook/Chromatic)
2507
+ - Generated code (test the generator, not the output)
2508
+
2509
+ Everything else gets tested. No excuses.
2510
+ ## STACK PROFILE: typescript.md
2511
+ Source: .agent-context/stacks/typescript.md
2512
+
2513
+ # TypeScript Stack Profile — The "Galak" Standard
2514
+
2515
+ > TypeScript is not JavaScript with optional types.
2516
+ > It is a contract system. Use it like one.
2517
+
2518
+ ## Compiler Configuration (Non-Negotiable)
2519
+
2520
+ ### tsconfig.json Strict Settings
2521
+ ```json
2522
+ {
2523
+ "compilerOptions": {
2524
+ "strict": true,
2525
+ "noUncheckedIndexedAccess": true,
2526
+ "noImplicitReturns": true,
2527
+ "noFallthroughCasesInSwitch": true,
2528
+ "noUnusedLocals": true,
2529
+ "noUnusedParameters": true,
2530
+ "exactOptionalPropertyTypes": true,
2531
+ "forceConsistentCasingInFileNames": true,
2532
+ "isolatedModules": true,
2533
+ "esModuleInterop": true,
2534
+ "resolveJsonModule": true,
2535
+ "moduleResolution": "bundler",
2536
+ "module": "ESNext",
2537
+ "target": "ES2022",
2538
+ "skipLibCheck": true,
2539
+ "paths": {
2540
+ "@/*": ["./src/*"]
2541
+ }
2542
+ }
2543
+ }
2544
+ ```
2545
+
2546
+ **Rule:** `"strict": true` is mandatory. If a project has `"strict": false`, fix it before writing any new code.
2547
+
2548
+ ---
2549
+
2550
+ ## The `any` Ban (Zero Tolerance)
2551
+
2552
+ ### Rule: `any` is BANNED. No exceptions.
2553
+
2554
+ ```typescript
2555
+ // ❌ INSTANT REJECTION
2556
+ function processData(data: any) { ... }
2557
+ const result = response.json() as any;
2558
+ // @ts-ignore
2559
+ // @ts-expect-error — only allowed with a comment explaining WHY and a linked issue
2560
+
2561
+ // ✅ REQUIRED: Use `unknown` with type narrowing
2562
+ function processData(data: unknown) {
2563
+ const parsed = DataSchema.parse(data); // Zod validates and narrows
2564
+ // `parsed` is now fully typed
2565
+ }
2566
+
2567
+ // ✅ REQUIRED: Use generics when the type varies
2568
+ function getFirstItem<T>(items: T[]): T | undefined {
2569
+ return items[0];
2570
+ }
2571
+ ```
2572
+
2573
+ ### What to Use Instead of `any`
2574
+ | Situation | Instead of `any`, Use |
2575
+ |-----------|----------------------|
2576
+ | Unknown external data | `unknown` + Zod parsing |
2577
+ | Generic container | `T` (generic type parameter) |
2578
+ | Object with unknown keys | `Record<string, unknown>` |
2579
+ | Function argument you'll refine | `unknown` + type guard |
2580
+ | Library with bad types | Write a `.d.ts` override or `unknown` wrapper |
2581
+ | Event handlers | The specific event type (`MouseEvent`, `ChangeEvent<HTMLInputElement>`) |
2582
+
2583
+ ---
2584
+
2585
+ ## Zod at Boundaries (Mandatory)
2586
+
2587
+ ### Rule: ALL External Data MUST Pass Through Zod
2588
+
2589
+ ```typescript
2590
+ // ❌ BANNED: Trusting external data
2591
+ app.post('/users', async (req, res) => {
2592
+ const { name, email, age } = req.body; // Could be anything!
2593
+ await userService.create({ name, email, age });
2594
+ });
2595
+
2596
+ // ✅ REQUIRED: Validate with Zod at the boundary
2597
+ import { z } from 'zod';
2598
+
2599
+ const CreateUserSchema = z.object({
2600
+ name: z.string().min(1).max(100).trim(),
2601
+ email: z.string().email().toLowerCase(),
2602
+ age: z.number().int().min(13).max(150),
2603
+ });
2604
+
2605
+ type CreateUserDto = z.infer<typeof CreateUserSchema>;
2606
+
2607
+ app.post('/users', async (req, res) => {
2608
+ const parsed = CreateUserSchema.safeParse(req.body);
2609
+ if (!parsed.success) {
2610
+ return res.status(400).json({ errors: parsed.error.flatten() });
2611
+ }
2612
+ // `parsed.data` is fully typed and validated
2613
+ await userService.create(parsed.data);
2614
+ });
2615
+ ```
2616
+
2617
+ ### Where Zod is MANDATORY
2618
+ - API request bodies (POST, PUT, PATCH)
2619
+ - Query parameters and path parameters
2620
+ - WebSocket incoming messages
2621
+ - Queue/event payloads from external systems
2622
+ - Environment variables at startup
2623
+ - Third-party API responses (trust but verify)
2624
+ - File upload metadata
2625
+
2626
+ ### Zod Best Practices
2627
+ ```typescript
2628
+ // ✅ Reuse schemas — define once, use everywhere
2629
+ // src/modules/user/user.schema.ts
2630
+ export const UserSchema = z.object({
2631
+ id: z.string().uuid(),
2632
+ email: z.string().email(),
2633
+ name: z.string().min(1).max(100),
2634
+ role: z.enum(['user', 'admin', 'moderator']),
2635
+ createdAt: z.coerce.date(),
2636
+ });
2637
+
2638
+ export const CreateUserSchema = UserSchema.omit({ id: true, createdAt: true });
2639
+ export const UpdateUserSchema = CreateUserSchema.partial();
2640
+
2641
+ // Types derived from schemas — single source of truth
2642
+ export type User = z.infer<typeof UserSchema>;
2643
+ export type CreateUserDto = z.infer<typeof CreateUserSchema>;
2644
+ export type UpdateUserDto = z.infer<typeof UpdateUserSchema>;
2645
+ ```
2646
+
2647
+ ---
2648
+
2649
+ ## API Documentation (Mandatory)
2650
+
2651
+ ### Rule: No API Endpoint Exists Without Documentation
2652
+
2653
+ Every API endpoint MUST have corresponding documentation. When creating or modifying an endpoint, you MUST simultaneously update the API documentation.
2654
+
2655
+ ### Preferred Approach: OpenAPI (Swagger)
2656
+
2657
+ ```typescript
2658
+ // Option 1: Code-first with decorators (NestJS)
2659
+ @ApiOperation({ summary: 'Create a new user' })
2660
+ @ApiBody({ type: CreateUserDto })
2661
+ @ApiResponse({ status: 201, description: 'User created', type: UserResponseDto })
2662
+ @ApiResponse({ status: 400, description: 'Validation error' })
2663
+ @ApiResponse({ status: 409, description: 'Email already exists' })
2664
+ @Post()
2665
+ async createUser(@Body() dto: CreateUserDto): Promise<UserResponseDto> { ... }
2666
+
2667
+ // Option 2: Schema-first with Zod + zod-to-openapi
2668
+ import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
2669
+ extendZodWithOpenApi(z);
2670
+
2671
+ const CreateUserSchema = z.object({
2672
+ name: z.string().min(1).openapi({ example: 'John Doe' }),
2673
+ email: z.string().email().openapi({ example: 'john@example.com' }),
2674
+ }).openapi('CreateUserRequest');
2675
+ ```
2676
+
2677
+ ### Documentation Checklist (Per Endpoint)
2678
+ - [ ] HTTP method and path
2679
+ - [ ] Request body schema (with examples)
2680
+ - [ ] Query/path parameter descriptions
2681
+ - [ ] All possible response codes with schemas
2682
+ - [ ] Authentication requirements
2683
+ - [ ] Rate limiting information (if applicable)
2684
+
2685
+ ### Documentation Must Stay in Sync
2686
+ ```
2687
+ ❌ BANNED:
2688
+ // Endpoint accepts `role` field but docs don't mention it
2689
+ // Endpoint returns 422 but docs only show 400
2690
+
2691
+ ✅ REQUIRED:
2692
+ // When you change an endpoint → update the schema/docs in the SAME commit
2693
+ // Use code-first tools so docs are generated from the actual types
2694
+ ```
2695
+
2696
+ ---
2697
+
2698
+ ## Import Style
2699
+
2700
+ ### Path Aliases (Required)
2701
+ ```typescript
2702
+ // ❌ BANNED: Deep relative imports
2703
+ import { UserService } from '../../../modules/user/user.service';
2704
+ import { AppError } from '../../../../shared/errors/app-error';
2705
+
2706
+ // ✅ REQUIRED: Path aliases
2707
+ import { UserService } from '@/modules/user/user.service';
2708
+ import { AppError } from '@/shared/errors/app-error';
2709
+ ```
2710
+
2711
+ ### Import Order (Enforced by ESLint)
2712
+ ```typescript
2713
+ // 1. Node built-ins
2714
+ import { readFile } from 'node:fs/promises';
2715
+ import { join } from 'node:path';
2716
+
2717
+ // 2. External packages
2718
+ import { z } from 'zod';
2719
+ import { PrismaClient } from '@prisma/client';
2720
+
2721
+ // 3. Internal modules (path aliases)
2722
+ import { UserService } from '@/modules/user/user.service';
2723
+ import { AppError } from '@/shared/errors/app-error';
2724
+
2725
+ // 4. Relative imports (same module only)
2726
+ import { CreateUserDto } from './user.dto';
2727
+ import { UserRepository } from './user.repository';
2728
+ ```
2729
+
2730
+ ---
2731
+
2732
+ ## Async Patterns
2733
+
2734
+ ### Rule: Prefer async/await Over Raw Promises
2735
+ ```typescript
2736
+ // ❌ BANNED: Promise chain hell
2737
+ function getUser(id: string) {
2738
+ return userRepo.findById(id)
2739
+ .then(user => {
2740
+ if (!user) throw new NotFoundError('User', id);
2741
+ return orderRepo.findByUserId(user.id);
2742
+ })
2743
+ .then(orders => ({ ...user, orders }))
2744
+ .catch(err => { throw err; });
2745
+ }
2746
+
2747
+ // ✅ REQUIRED: Clean async/await
2748
+ async function getUser(id: string): Promise<UserWithOrders> {
2749
+ const user = await userRepo.findById(id);
2750
+ if (!user) throw new NotFoundError('User', id);
2751
+
2752
+ const orders = await orderRepo.findByUserId(user.id);
2753
+ return { ...user, orders };
2754
+ }
2755
+ ```
2756
+
2757
+ ### Parallel Execution
2758
+ ```typescript
2759
+ // ❌ SLOW: Sequential when operations are independent
2760
+ const user = await getUser(id);
2761
+ const orders = await getOrders(id);
2762
+ const preferences = await getPreferences(id);
2763
+
2764
+ // ✅ FAST: Parallel independent operations
2765
+ const [user, orders, preferences] = await Promise.all([
2766
+ getUser(id),
2767
+ getOrders(id),
2768
+ getPreferences(id),
2769
+ ]);
2770
+ ```
2771
+
2772
+ ---
2773
+
2774
+ ## Enum and Union Types
2775
+
2776
+ ### Prefer `as const` Unions Over Enums
2777
+ ```typescript
2778
+ // ⚠️ ACCEPTABLE but verbose:
2779
+ enum OrderStatus {
2780
+ PENDING = 'pending',
2781
+ CONFIRMED = 'confirmed',
2782
+ SHIPPED = 'shipped',
2783
+ DELIVERED = 'delivered',
2784
+ }
2785
+
2786
+ // ✅ PREFERRED: const assertion + union type
2787
+ const ORDER_STATUSES = ['pending', 'confirmed', 'shipped', 'delivered'] as const;
2788
+ type OrderStatus = (typeof ORDER_STATUSES)[number];
2789
+ // OrderStatus = 'pending' | 'confirmed' | 'shipped' | 'delivered'
2790
+
2791
+ // Works perfectly with Zod:
2792
+ const OrderStatusSchema = z.enum(ORDER_STATUSES);
2793
+ ```
2794
+
2795
+ ---
2796
+
2797
+ ## Preferred Libraries (V1.0 — 2025)
2798
+
2799
+ | Need | Library | Why |
2800
+ |------|---------|-----|
2801
+ | Runtime | Bun / Node 20+ | ESM native, fast, batteries-included |
2802
+ | Validation | `zod` | 0 deps, type inference, composable |
2803
+ | ORM | `prisma` or `drizzle-orm` | Type-safe queries, migration support |
2804
+ | HTTP framework | `hono` / `fastify` / Next.js | Lightweight, modern, tree-shakeable |
2805
+ | Testing | `vitest` | Vite-native, Jest-compatible API, fast |
2806
+ | Linting | `eslint` + `@typescript-eslint` | Community standard |
2807
+ | Formatting | `prettier` | Community standard |
2808
+ | Date | `date-fns` or `Temporal` (when stable) | Tree-shakeable, immutable |
2809
+ | HTTP client | `ky` / `ofetch` / built-in `fetch` | Lightweight, modern |
2810
+ | Password | `bcrypt` / `argon2` | Proven, secure |
2811
+ | Logger | `pino` | Fastest JSON logger for Node.js |
2812
+ | Env | `@t3-oss/env-core` + Zod | Type-safe env validation |
2813
+
2814
+ ---
2815
+
2816
+ ## Banned Patterns
2817
+
2818
+ | Pattern | Why | Alternative |
2819
+ |---------|-----|-------------|
2820
+ | `any` type | Defeats TypeScript's purpose | `unknown` + narrowing |
2821
+ | `// @ts-ignore` | Hides real type errors | Fix the type or `@ts-expect-error` with comment |
2822
+ | `var` keyword | Function scoping bugs | `const` (default) or `let` |
2823
+ | `==` loose equality | Type coercion surprises | `===` always |
2824
+ | `console.log` in production | Not structured, not configurable | Use `pino` or structured logger |
2825
+ | `new Date()` without timezone | Timezone bugs | Explicit UTC or use date-fns |
2826
+ | Default exports | Naming inconsistency across imports | Named exports only |
2827
+ | Barrel re-exports (`index.ts`) | Circular dependency magnets | Direct imports or module public API only |
2828
+ | `moment.js` | Deprecated, massive bundle | `date-fns` or `dayjs` |
2829
+ | `class` with inheritance (deep chains) | Fragile hierarchies | Composition + interfaces |
2830
+ ## BLUEPRINT PROFILE: api-nextjs.md
2831
+ Source: .agent-context/blueprints/api-nextjs.md
2832
+
2833
+ # Blueprint: Next.js API Project (App Router)
2834
+
2835
+ > This blueprint defines how to scaffold a Next.js API project from scratch.
2836
+ > Follow this structure exactly. Do not deviate.
2837
+
2838
+ ## Tech Stack
2839
+ - **Runtime:** Node.js 20+ / Bun
2840
+ - **Framework:** Next.js 14+ (App Router)
2841
+ - **Validation:** Zod
2842
+ - **ORM:** Prisma (or Drizzle)
2843
+ - **Auth:** NextAuth.js v5 / Lucia Auth
2844
+ - **Testing:** Vitest
2845
+
2846
+ ## Project Structure
2847
+
2848
+ ```
2849
+ project-name/
2850
+ ├── src/
2851
+ │ ├── app/ # Next.js App Router
2852
+ │ │ ├── api/ # API routes
2853
+ │ │ │ ├── auth/
2854
+ │ │ │ │ └── [...nextauth]/
2855
+ │ │ │ │ └── route.ts # Auth handler
2856
+ │ │ │ ├── users/
2857
+ │ │ │ │ ├── route.ts # GET /api/users, POST /api/users
2858
+ │ │ │ │ └── [id]/
2859
+ │ │ │ │ └── route.ts # GET/PUT/DELETE /api/users/:id
2860
+ │ │ │ └── health/
2861
+ │ │ │ └── route.ts # GET /api/health
2862
+ │ │ ├── layout.tsx # Root layout
2863
+ │ │ └── page.tsx # Root page
2864
+ │ │
2865
+ │ ├── modules/ # Feature modules (domain-driven)
2866
+ │ │ ├── user/
2867
+ │ │ │ ├── user.service.ts # Business logic
2868
+ │ │ │ ├── user.repository.ts # Data access
2869
+ │ │ │ ├── user.schema.ts # Zod schemas + DTOs
2870
+ │ │ │ ├── user.types.ts # Type definitions
2871
+ │ │ │ └── __tests__/
2872
+ │ │ │ └── user.service.test.ts
2873
+ │ │ └── order/
2874
+ │ │ ├── order.service.ts
2875
+ │ │ ├── order.repository.ts
2876
+ │ │ ├── order.schema.ts
2877
+ │ │ └── order.types.ts
2878
+ │ │
2879
+ │ ├── shared/ # Cross-cutting concerns
2880
+ │ │ ├── config/
2881
+ │ │ │ └── env.ts # Zod-validated environment variables
2882
+ │ │ ├── errors/
2883
+ │ │ │ ├── app-error.ts # Base error class
2884
+ │ │ │ └── error-handler.ts # Global error handler for API routes
2885
+ │ │ ├── middleware/
2886
+ │ │ │ ├── auth.ts # Auth middleware
2887
+ │ │ │ └── validate.ts # Request validation middleware
2888
+ │ │ ├── lib/
2889
+ │ │ │ ├── prisma.ts # Prisma client singleton
2890
+ │ │ │ └── logger.ts # Pino logger setup
2891
+ │ │ └── types/
2892
+ │ │ └── api.ts # Shared API types (ApiResponse, etc.)
2893
+ │ │
2894
+ │ └── components/ # UI components (if full-stack)
2895
+ │ ├── ui/ # Primitives
2896
+ │ └── layout/ # Layout components
2897
+
2898
+ ├── prisma/
2899
+ │ ├── schema.prisma # Database schema
2900
+ │ └── migrations/ # Database migrations
2901
+
2902
+ ├── public/ # Static assets
2903
+ ├── .env.example # Environment template
2904
+ ├── .eslintrc.json # ESLint config
2905
+ ├── .prettierrc # Prettier config
2906
+ ├── next.config.mjs # Next.js config
2907
+ ├── tsconfig.json # TypeScript config (strict!)
2908
+ ├── vitest.config.ts # Vitest config
2909
+ └── package.json
2910
+ ```
2911
+
2912
+ ## API Route Handler Pattern
2913
+
2914
+ ```typescript
2915
+ // src/app/api/users/route.ts
2916
+ import { NextRequest, NextResponse } from 'next/server';
2917
+ import { CreateUserSchema } from '@/modules/user/user.schema';
2918
+ import { userService } from '@/modules/user/user.service';
2919
+ import { handleApiError } from '@/shared/errors/error-handler';
2920
+
2921
+ export async function GET(request: NextRequest) {
2922
+ try {
2923
+ const searchParams = request.nextUrl.searchParams;
2924
+ const page = Number(searchParams.get('page') ?? '1');
2925
+ const limit = Number(searchParams.get('limit') ?? '20');
2926
+
2927
+ const users = await userService.findAll({ page, limit });
2928
+ return NextResponse.json({ data: users });
2929
+ } catch (error) {
2930
+ return handleApiError(error);
2931
+ }
2932
+ }
2933
+
2934
+ export async function POST(request: NextRequest) {
2935
+ try {
2936
+ const body = await request.json();
2937
+ const parsed = CreateUserSchema.safeParse(body);
2938
+
2939
+ if (!parsed.success) {
2940
+ return NextResponse.json(
2941
+ { error: { code: 'VALIDATION_ERROR', details: parsed.error.flatten() } },
2942
+ { status: 400 },
2943
+ );
2944
+ }
2945
+
2946
+ const user = await userService.create(parsed.data);
2947
+ return NextResponse.json({ data: user }, { status: 201 });
2948
+ } catch (error) {
2949
+ return handleApiError(error);
2950
+ }
2951
+ }
2952
+ ```
2953
+
2954
+ ## Environment Validation Pattern
2955
+
2956
+ ```typescript
2957
+ // src/shared/config/env.ts
2958
+ import { z } from 'zod';
2959
+
2960
+ const envSchema = z.object({
2961
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
2962
+ DATABASE_URL: z.string().url(),
2963
+ NEXTAUTH_SECRET: z.string().min(32),
2964
+ NEXTAUTH_URL: z.string().url(),
2965
+ PORT: z.coerce.number().default(3000),
2966
+ });
2967
+
2968
+ export const env = envSchema.parse(process.env);
2969
+ export type Env = z.infer<typeof envSchema>;
2970
+ ```
2971
+
2972
+ ## Error Handler Pattern
2973
+
2974
+ ```typescript
2975
+ // src/shared/errors/error-handler.ts
2976
+ import { NextResponse } from 'next/server';
2977
+ import { AppError } from './app-error';
2978
+ import { logger } from '@/shared/lib/logger';
2979
+
2980
+ export function handleApiError(error: unknown): NextResponse {
2981
+ if (error instanceof AppError) {
2982
+ logger.warn('Application error', {
2983
+ code: error.code,
2984
+ message: error.message,
2985
+ context: error.context,
2986
+ });
2987
+
2988
+ return NextResponse.json(
2989
+ { error: { code: error.code, message: error.message } },
2990
+ { status: error.statusCode },
2991
+ );
2992
+ }
2993
+
2994
+ logger.error('Unhandled error', {
2995
+ error: error instanceof Error ? error.message : String(error),
2996
+ stack: error instanceof Error ? error.stack : undefined,
2997
+ });
2998
+
2999
+ return NextResponse.json(
3000
+ { error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } },
3001
+ { status: 500 },
3002
+ );
3003
+ }
3004
+ ```
3005
+
3006
+ ## Scaffolding Checklist
3007
+
3008
+ When using this blueprint, verify:
3009
+ - [ ] `tsconfig.json` has `"strict": true` and all recommended flags
3010
+ - [ ] `.env.example` exists with ALL required variables (placeholder values only)
3011
+ - [ ] Prisma client is a singleton (not re-created per request)
3012
+ - [ ] All API routes use Zod validation at the boundary
3013
+ - [ ] Error handler is used in every route (no unhandled errors)
3014
+ - [ ] Logger is configured (not `console.log`)
3015
+ - [ ] Path aliases (`@/`) are configured in tsconfig AND next.config
3016
+ - [ ] API documentation is generated or maintained alongside routes
3017
+ ## CI/CD GUARDRAILS: ci-github-actions.md
3018
+ Source: .agent-context/blueprints/ci-github-actions.md
3019
+
3020
+ # Blueprint: GitHub Actions CI/CD Pipeline
3021
+
3022
+ > Automate everything. Trust nothing. Ship with confidence.
3023
+
3024
+ ## Tech Stack
3025
+
3026
+ | Layer | Tool |
3027
+ |-------|------|
3028
+ | CI/CD | GitHub Actions |
3029
+ | Runners | GitHub-hosted (ubuntu-latest) |
3030
+ | Caching | actions/cache, setup-node cache |
3031
+ | Security | OIDC for cloud auth, pinned action SHAs |
3032
+ | Artifacts | actions/upload-artifact |
3033
+
3034
+ ## Pipeline Architecture
3035
+
3036
+ ```
3037
+ ┌─────────────────────────────────────────────────────────┐
3038
+ │ PR / Push Trigger │
3039
+ ├──────────┬──────────┬──────────┬──────────┬─────────────┬─────────────┤
3040
+ │ Lint │ Build │ Test │ Security │ Docs │ LLM Judge │
3041
+ │ (ESLint/ │ (tsc / │ (Vitest/ │ (Audit / │ (OpenAPI │ (Checklist │
3042
+ │ ruff) │ build) │ pytest) │ Trivy) │ validate) │ enforcement)│
3043
+ ├──────────┴──────────┴──────────┴──────────┴─────────────┴─────────────┤
3044
+ │ Gate: All jobs must pass │
3045
+ ├─────────────────────────────────────────────────────────┤
3046
+ │ Deploy (on main only) │
3047
+ │ Staging → Smoke Tests → Production (manual approval) │
3048
+ └─────────────────────────────────────────────────────────┘
3049
+ ```
3050
+
3051
+ ## Required Workflows
3052
+
3053
+ ### 1. CI Workflow (`ci.yml`)
3054
+
3055
+ Runs on every PR and push to main.
3056
+
3057
+ ```yaml
3058
+ name: CI
3059
+
3060
+ on:
3061
+ pull_request:
3062
+ branches: [main]
3063
+ push:
3064
+ branches: [main]
3065
+
3066
+ permissions:
3067
+ contents: read
3068
+
3069
+ concurrency:
3070
+ group: ci-${{ github.ref }}
3071
+ cancel-in-progress: true
3072
+
3073
+ jobs:
3074
+ lint:
3075
+ runs-on: ubuntu-latest
3076
+ timeout-minutes: 10
3077
+ steps:
3078
+ - uses: actions/checkout@<pin-sha>
3079
+ - uses: actions/setup-node@<pin-sha>
3080
+ with:
3081
+ node-version-file: '.nvmrc'
3082
+ cache: 'npm'
3083
+ - run: npm ci
3084
+ - run: npm run lint
3085
+ - run: npm run type-check
3086
+
3087
+ test:
3088
+ runs-on: ubuntu-latest
3089
+ timeout-minutes: 15
3090
+ steps:
3091
+ - uses: actions/checkout@<pin-sha>
3092
+ - uses: actions/setup-node@<pin-sha>
3093
+ with:
3094
+ node-version-file: '.nvmrc'
3095
+ cache: 'npm'
3096
+ - run: npm ci
3097
+ - run: npm run test -- --coverage
3098
+ - uses: actions/upload-artifact@<pin-sha>
3099
+ with:
3100
+ name: coverage
3101
+ path: coverage/
3102
+
3103
+ security:
3104
+ runs-on: ubuntu-latest
3105
+ timeout-minutes: 10
3106
+ steps:
3107
+ - uses: actions/checkout@<pin-sha>
3108
+ - run: npm audit --audit-level=high
3109
+ # Add Trivy, CodeQL, or Snyk as needed
3110
+
3111
+ build:
3112
+ needs: [lint, test, security]
3113
+ runs-on: ubuntu-latest
3114
+ timeout-minutes: 15
3115
+ steps:
3116
+ - uses: actions/checkout@<pin-sha>
3117
+ - uses: actions/setup-node@<pin-sha>
3118
+ with:
3119
+ node-version-file: '.nvmrc'
3120
+ cache: 'npm'
3121
+ - run: npm ci
3122
+ - run: npm run build
3123
+ - uses: actions/upload-artifact@<pin-sha>
3124
+ with:
3125
+ name: build
3126
+ path: dist/
3127
+
3128
+ llm-judge:
3129
+ needs: [lint, test, security, build]
3130
+ runs-on: ubuntu-latest
3131
+ timeout-minutes: 12
3132
+ env:
3133
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
3134
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
3135
+ GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
3136
+ GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
3137
+ GITHUB_HEAD_SHA: ${{ github.sha }}
3138
+ steps:
3139
+ - uses: actions/checkout@<pin-sha>
3140
+ with:
3141
+ fetch-depth: 0 # Full history required for accurate git diff
3142
+ - uses: actions/setup-node@<pin-sha>
3143
+ with:
3144
+ node-version: '22'
3145
+ - name: Run LLM Judge
3146
+ run: node scripts/llm-judge.mjs
3147
+ - name: Upload LLM Judge machine report
3148
+ if: always()
3149
+ uses: actions/upload-artifact@<pin-sha>
3150
+ with:
3151
+ name: llm-judge-report
3152
+ path: .agent-context/state/llm-judge-report.json
3153
+ ```
3154
+
3155
+ ### 2. Deploy Workflow (`deploy.yml`)
3156
+
3157
+ Runs on push to main (after CI passes).
3158
+
3159
+ ```yaml
3160
+ name: Deploy
3161
+
3162
+ on:
3163
+ workflow_run:
3164
+ workflows: [CI]
3165
+ types: [completed]
3166
+ branches: [main]
3167
+
3168
+ permissions:
3169
+ id-token: write # OIDC
3170
+ contents: read
3171
+
3172
+ jobs:
3173
+ deploy-staging:
3174
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
3175
+ runs-on: ubuntu-latest
3176
+ environment: staging
3177
+ steps:
3178
+ # Authenticate via OIDC — no static secrets
3179
+ # Deploy to staging
3180
+ # Run smoke tests against staging
3181
+
3182
+ deploy-production:
3183
+ needs: deploy-staging
3184
+ runs-on: ubuntu-latest
3185
+ environment:
3186
+ name: production
3187
+ url: https://your-app.com
3188
+ steps:
3189
+ # Deploy to production
3190
+ # Run health checks
3191
+ # Notify team (Slack, Discord)
3192
+ ```
3193
+
3194
+ ## Security Rules
3195
+
3196
+ 1. **Pin actions to commit SHA** — not `@v3`, not `@latest`
3197
+ 2. **Use OIDC** for cloud provider auth — delete static credentials
3198
+ 3. **Minimal permissions** — set `permissions:` at workflow level, least privilege
3199
+ 4. **Never print secrets** — GitHub masks them in logs, but don't `echo` them
3200
+ 5. **Restrict self-hosted runners** — ephemeral containers only, never persistent VMs
3201
+ 6. **Require PR approval** for workflows from forks
3202
+ 7. **Minimize prompt scope** — send diff + checklist only, never full secret-bearing config
3203
+
3204
+ ## LLM Judge Annotation Contract
3205
+
3206
+ The judge emits a machine-friendly payload line and artifact that can be consumed by annotation scripts:
3207
+
3208
+ - Log line: `JSON_REPORT: { ... }`
3209
+ - Artifact file: `.agent-context/state/llm-judge-report.json`
3210
+ - Normalized severity values: `critical`, `high`, `medium`, `low`
3211
+ - Override artifact path (optional): `LLM_JUDGE_OUTPUT_PATH`
3212
+
3213
+ ## Efficiency Rules
3214
+
3215
+ 1. **Cache dependencies** — `actions/cache` or setup-action built-in cache
3216
+ 2. **Use concurrency** — cancel previous runs on the same branch
3217
+ 3. **Set timeouts** — prevent stuck jobs from burning minutes
3218
+ 4. **Matrix builds** for multi-platform/version testing
3219
+ 5. **Reusable workflows** — centralize common pipelines in a `.github` repo
3220
+ 6. **Skip unnecessary jobs** — use path filters for targeted CI
3221
+
3222
+ ```yaml
3223
+ # Example: Only run frontend tests when frontend code changes
3224
+ on:
3225
+ push:
3226
+ paths:
3227
+ - 'src/frontend/**'
3228
+ - 'package.json'
3229
+ ```
3230
+
3231
+ ## Scaffolding Checklist
3232
+
3233
+ When setting up GitHub Actions for a new project:
3234
+
3235
+ - [ ] Create `.github/workflows/ci.yml` with lint, test, build, security
3236
+ - [ ] Create `.github/workflows/deploy.yml` with staging + production
3237
+ - [ ] Pin ALL third-party actions to commit SHA
3238
+ - [ ] Set `permissions: contents: read` at workflow level
3239
+ - [ ] Configure `concurrency` to cancel in-progress runs
3240
+ - [ ] Add `timeout-minutes` to every job
3241
+ - [ ] Set up caching for package manager
3242
+ - [ ] Configure environment protection rules for production
3243
+ - [ ] Create `.nvmrc` or `.tool-versions` for runtime version
3244
+ - [ ] Configure branch protection: require CI pass before merge
3245
+ - [ ] Add `llm-judge` job that evaluates PR against `pr-checklist.md`
3246
+ ## CI/CD GUARDRAILS: ci-gitlab.md
3247
+ Source: .agent-context/blueprints/ci-gitlab.md
3248
+
3249
+ # Blueprint: GitLab CI/CD Pipeline
3250
+
3251
+ > Same principles as GitHub Actions, adapted for GitLab's pipeline model.
3252
+
3253
+ ## Tech Stack
3254
+
3255
+ | Layer | Tool |
3256
+ |-------|------|
3257
+ | CI/CD | GitLab CI/CD |
3258
+ | Config | `.gitlab-ci.yml` |
3259
+ | Runners | Shared runners or dedicated (Docker executor) |
3260
+ | Registry | GitLab Container Registry |
3261
+ | Caching | GitLab CI cache (per-branch, per-job) |
3262
+
3263
+ ## Pipeline Architecture
3264
+
3265
+ ```yaml
3266
+ stages:
3267
+ - validate # Lint + type check
3268
+ - test # Unit + integration tests
3269
+ - security # Dependency audit + SAST
3270
+ - build # Compile, bundle, containerize
3271
+ - judge # LLM-as-a-Judge checklist gate
3272
+ - deploy # Staging → Production
3273
+ ```
3274
+
3275
+ ## Pipeline Template (`.gitlab-ci.yml`)
3276
+
3277
+ ```yaml
3278
+ default:
3279
+ image: node:22-alpine
3280
+ cache:
3281
+ key:
3282
+ files: [package-lock.json]
3283
+ paths: [node_modules/]
3284
+ policy: pull-push
3285
+
3286
+ stages:
3287
+ - validate
3288
+ - test
3289
+ - security
3290
+ - build
3291
+ - judge
3292
+ - deploy
3293
+
3294
+ # ─── VALIDATE ───────────────────────────────────────────
3295
+ lint:
3296
+ stage: validate
3297
+ script:
3298
+ - npm ci --prefer-offline
3299
+ - npm run lint
3300
+ - npm run type-check
3301
+ rules:
3302
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
3303
+ - if: '$CI_COMMIT_BRANCH == "main"'
3304
+
3305
+ # ─── TEST ───────────────────────────────────────────────
3306
+ test:unit:
3307
+ stage: test
3308
+ script:
3309
+ - npm ci --prefer-offline
3310
+ - npm run test -- --coverage
3311
+ coverage: '/All files\s*\|\s*(\d+\.?\d*)\s*\|/'
3312
+ artifacts:
3313
+ reports:
3314
+ coverage_report:
3315
+ coverage_format: cobertura
3316
+ path: coverage/cobertura-coverage.xml
3317
+ paths: [coverage/]
3318
+ expire_in: 7 days
3319
+
3320
+ test:integration:
3321
+ stage: test
3322
+ services:
3323
+ - postgres:16-alpine
3324
+ variables:
3325
+ POSTGRES_DB: test_db
3326
+ POSTGRES_USER: test_user
3327
+ POSTGRES_PASSWORD: test_pass
3328
+ DATABASE_URL: "postgresql://test_user:test_pass@postgres:5432/test_db"
3329
+ script:
3330
+ - npm ci --prefer-offline
3331
+ - npm run test:integration
3332
+
3333
+ # ─── SECURITY ───────────────────────────────────────────
3334
+ audit:
3335
+ stage: security
3336
+ script:
3337
+ - npm audit --audit-level=high
3338
+ allow_failure: false
3339
+
3340
+ sast:
3341
+ stage: security
3342
+ # GitLab's built-in SAST template
3343
+ include:
3344
+ - template: Security/SAST.gitlab-ci.yml
3345
+
3346
+ # ─── BUILD ──────────────────────────────────────────────
3347
+ build:
3348
+ stage: build
3349
+ script:
3350
+ - npm ci --prefer-offline
3351
+ - npm run build
3352
+ artifacts:
3353
+ paths: [dist/]
3354
+ expire_in: 1 day
3355
+ rules:
3356
+ - if: '$CI_COMMIT_BRANCH == "main"'
3357
+
3358
+ # ─── LLM JUDGE ──────────────────────────────────────────
3359
+ llm:judge:
3360
+ stage: judge
3361
+ image: node:22-alpine
3362
+ variables:
3363
+ OPENAI_API_KEY: $OPENAI_API_KEY
3364
+ ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
3365
+ GEMINI_API_KEY: $GEMINI_API_KEY
3366
+ # CI_MERGE_REQUEST_DIFF_BASE_SHA and CI_COMMIT_SHA are set automatically
3367
+ # by GitLab for merge request pipelines — no manual configuration needed.
3368
+ before_script:
3369
+ - git fetch --unshallow || true # Ensure full history for git diff
3370
+ script:
3371
+ - node scripts/llm-judge.mjs
3372
+ artifacts:
3373
+ when: always
3374
+ paths:
3375
+ - .agent-context/state/llm-judge-report.json
3376
+ expire_in: 7 days
3377
+ rules:
3378
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
3379
+
3380
+ # ─── DEPLOY ─────────────────────────────────────────────
3381
+ deploy:staging:
3382
+ stage: deploy
3383
+ environment:
3384
+ name: staging
3385
+ url: https://staging.example.com
3386
+ script:
3387
+ - echo "Deploy to staging"
3388
+ # Add your deployment commands
3389
+ rules:
3390
+ - if: '$CI_COMMIT_BRANCH == "main"'
3391
+
3392
+ deploy:production:
3393
+ stage: deploy
3394
+ environment:
3395
+ name: production
3396
+ url: https://example.com
3397
+ script:
3398
+ - echo "Deploy to production"
3399
+ # Add your deployment commands
3400
+ rules:
3401
+ - if: '$CI_COMMIT_BRANCH == "main"'
3402
+ when: manual # Require manual approval
3403
+ needs: [deploy:staging]
3404
+ ```
3405
+
3406
+ ## Key Differences from GitHub Actions
3407
+
3408
+ | Concern | GitHub Actions | GitLab CI |
3409
+ |---------|---------------|-----------|
3410
+ | Config file | `.github/workflows/*.yml` | `.gitlab-ci.yml` (single file) |
3411
+ | Jobs linkage | `needs:` | `stages:` (sequential) + `needs:` (DAG) |
3412
+ | Secrets | GitHub Secrets | CI/CD Variables (masked + protected) |
3413
+ | Caching | `actions/cache` | Built-in `cache:` directive |
3414
+ | Services | Docker Compose / service containers | `services:` directive |
3415
+ | Environments | Environment protection rules | Environment + `when: manual` |
3416
+ | Includes | Reusable workflows | `include:` with `template:` |
3417
+
3418
+ ## Security Rules
3419
+
3420
+ 1. **Protected variables** — mark secrets as Protected + Masked
3421
+ 2. **Protected branches** — only deploy from protected branches
3422
+ 3. **Include GitLab SAST/DAST** templates for automated scanning
3423
+ 4. **Limit runner access** — use tags to route jobs to appropriate runners
3424
+ 5. **Artifact expiration** — set `expire_in` on all artifacts
3425
+ 6. **Limit LLM input scope** — send only merge diff + checklist context
3426
+
3427
+ ## Scaffolding Checklist
3428
+
3429
+ - [ ] Create `.gitlab-ci.yml` with validate, test, security, build, deploy stages
3430
+ - [ ] Configure CI/CD variables for secrets (masked + protected)
3431
+ - [ ] Set up caching for package manager lockfile
3432
+ - [ ] Add coverage reporting with Cobertura format
3433
+ - [ ] Configure environments (staging, production) with manual approval
3434
+ - [ ] Include SAST template for security scanning
3435
+ - [ ] Set up merge request pipelines with `rules:`
3436
+ - [ ] Configure branch protection rules
3437
+ - [ ] Add `timeout` to long-running jobs
3438
+ - [ ] Use `needs:` for DAG optimization where possible
3439
+ - [ ] Add `llm:judge` stage that enforces `pr-checklist.md`
3440
+
3441
+ ## LLM Judge Annotation Contract
3442
+
3443
+ For MR annotations and dashboards, parse either:
3444
+
3445
+ - Log line: `JSON_REPORT: { ... }`
3446
+ - Artifact: `.agent-context/state/llm-judge-report.json`
3447
+
3448
+ Severity values are normalized by the judge to: `critical`, `high`, `medium`, `low`.
3449
+ ## SKILL PACK: Frontend
3450
+ Source: .agent-context/skills/frontend.md
3451
+ Default tier: advance
3452
+ Selected tier: advance
3453
+ Evidence: Frontend usability audit, accessibility checks, and visual regression output.
3454
+
3455
+ # Frontend Skill Pack
3456
+
3457
+ Default tier: `advance`
3458
+
3459
+ ## Purpose
3460
+ Deliver frontend experiences that are visually intentional, accessible, and efficient.
3461
+
3462
+ ## In Scope
3463
+ - UI architecture and component composition
3464
+ - Responsive layouts and breakpoints
3465
+ - Motion, animation, and interaction polish
3466
+ - Accessibility and keyboard flows
3467
+ - Conversion clarity and onboarding flow
3468
+
3469
+ ## Must-Have Checks
3470
+ - Feature-driven folder structure
3471
+ - Smart and dumb component separation
3472
+ - Server state and client state separation
3473
+ - Reduced-motion fallback
3474
+ - Accessibility baseline pass
3475
+ - Responsive behavior verified on mobile and desktop
3476
+
3477
+ ## Evidence
3478
+ - Usability audit result
3479
+ - Visual regression output
3480
+ - Accessibility checklist result
3481
+ - Release notes for motion and interaction changes
3482
+
3483
+ ## Fallback
3484
+ - If a feature cannot meet the advance tier, ship standard mode only as a temporary compatibility path.
3485
+
3486
+ ## SKILL PACK: Fullstack
3487
+ Source: .agent-context/skills/fullstack.md
3488
+ Default tier: advance
3489
+ Selected tier: advance
3490
+ Evidence: End-to-end tests, contract validation, and feature parity review.
3491
+
3492
+ # Fullstack Skill Pack
3493
+
3494
+ Default tier: `advance`
3495
+
3496
+ ## Purpose
3497
+ Coordinate frontend and backend delivery as a single product system.
3498
+
3499
+ ## In Scope
3500
+ - Feature slicing across UI and API boundaries
3501
+ - Shared validation contracts
3502
+ - End-to-end flows and release readiness
3503
+ - Performance, accessibility, and observability together
3504
+
3505
+ ## Must-Have Checks
3506
+ - Single feature directory with clear public API
3507
+ - Frontend and backend contracts aligned
3508
+ - End-to-end test coverage for critical paths
3509
+ - Release notes explain UX and API impact together
3510
+
3511
+ ## Evidence
3512
+ - Feature parity checklist
3513
+ - End-to-end test report
3514
+ - Contract validation output
3515
+ - Release artifact bundle
3516
+
3517
+ ## Fallback
3518
+ - Split delivery only when the feature boundary is explicit and the evidence bundle is still complete.
3519
+
3520
+ ## SKILL PACK: CLI
3521
+ Source: .agent-context/skills/cli.md
3522
+ Default tier: advance
3523
+ Selected tier: advance
3524
+ Evidence: CLI smoke tests, dry-run output, and automation-friendly reports.
3525
+
3526
+ # CLI Skill Pack
3527
+
3528
+ Default tier: `advance`
3529
+
3530
+ ## Purpose
3531
+ Create smart command-line workflows that guide users efficiently and safely.
3532
+
3533
+ ## In Scope
3534
+ - Interactive initialization and upgrade flows
3535
+ - Safe defaults and confirmation steps
3536
+ - Machine-readable output for automation
3537
+ - Validation and self-healing hooks
3538
+ - Cross-platform shell behavior
3539
+
3540
+ ## Must-Have Checks
3541
+ - Explicit command help and examples
3542
+ - Deterministic output format for automation
3543
+ - Safe destructive-action guards
3544
+ - Validation before mutation
3545
+ - Exit codes reflect success and failure clearly
3546
+
3547
+ ## Evidence
3548
+ - CLI smoke tests
3549
+ - Machine-readable report output
3550
+ - Upgrade dry-run output
3551
+ - Cross-platform execution notes
3552
+
3553
+ ## Fallback
3554
+ - Standard mode can remain available for compatibility, but advance is the default user experience.
3555
+
3556
+ ## STATE MAP: architecture-map.md
3557
+ Source: .agent-context/state/architecture-map.md
3558
+
3559
+ # Architecture Map (State Awareness)
3560
+
3561
+ > This file defines protected architectural boundaries for AI-assisted changes.
3562
+
3563
+ ## Boundary Classification
3564
+
3565
+ | Module/Path Pattern | Criticality | Change Policy | Required Checks |
3566
+ |---------------------|-------------|---------------|-----------------|
3567
+ | `src/modules/payment/**` | critical | Must preserve transactional behavior and idempotency | Unit + integration + rollback test |
3568
+ | `src/modules/authentication/**` | critical | Never bypass auth guards or token validation | Security audit + integration tests |
3569
+ | `src/modules/**/repository/**` | high | Preserve query contracts and avoid N+1 regressions | Query plan review + performance audit |
3570
+ | `src/features/**` | medium | Keep UI contracts stable and avoid API drift | Component tests + contract checks |
3571
+ | `src/shared/**` | high | Backward compatibility required for public utilities | Cross-module usage validation |
3572
+
3573
+ ## Required Agent Behavior
3574
+
3575
+ 1. Before editing a `critical` area, load `.agent-context/review-checklists/security-audit.md` and `.agent-context/review-checklists/performance-audit.md`.
3576
+ 2. For boundary-crossing changes, verify no circular dependencies are introduced (see `dependency-map.md`).
3577
+ 3. Every critical-path change must include explicit risk notes in PR description.
3578
+
3579
+ ## Project-Specific Notes
3580
+
3581
+ - Replace placeholder path patterns with your actual module map.
3582
+ - Mark payment, identity, and financial reconciliation flows as `critical`.
3583
+ - Keep this file updated whenever module ownership changes.
3584
+ ## STATE MAP: dependency-map.md
3585
+ Source: .agent-context/state/dependency-map.md
3586
+
3587
+ # Dependency Map (State Awareness)
3588
+
3589
+ > This map documents allowed dependency direction to prevent circular references during refactors.
3590
+
3591
+ ## Layer Dependency Rules
3592
+
3593
+ 1. Transport layer may depend on Service layer.
3594
+ 2. Service layer may depend on Domain contracts and Repository interfaces.
3595
+ 3. Infrastructure layer may implement Repository interfaces.
3596
+ 4. Domain layer must not depend on Transport or Infrastructure.
3597
+
3598
+ ## Module-Level Constraints
3599
+
3600
+ | Source Module | Allowed Dependencies | Forbidden Dependencies |
3601
+ |---------------|----------------------|------------------------|
3602
+ | `authentication` | `shared`, `user` | `payment` internals |
3603
+ | `payment` | `shared`, `billing`, `notification` contracts | `authentication` internals |
3604
+ | `reporting` | `shared`, read-only repository ports | write-side service internals |
3605
+ | `frontend` | public API clients only | direct repository access |
3606
+
3607
+ ## Circular Dependency Guardrail
3608
+
3609
+ When refactoring:
3610
+
3611
+ 1. Detect import graph changes before applying bulk edits.
3612
+ 2. Reject any change introducing `A -> B -> A` cycles.
3613
+ 3. Move shared contracts to `shared` module when two-way dependencies appear.
3614
+
3615
+ ## Project-Specific Notes
3616
+
3617
+ - Replace sample modules with your real domain modules.
3618
+ - Keep this map synchronized with architecture decisions and ADRs.
3619
+ ## REVIEW CHECKLIST: pr-checklist.md
3620
+ Source: .agent-context/review-checklists/pr-checklist.md
3621
+
3622
+ # PR Checklist — The Quality Gate
3623
+
3624
+ > Run this before declaring any task "done."
3625
+ > If ANY item fails, the task is NOT complete.
3626
+
3627
+ ## Instructions for Agent
3628
+
3629
+ When asked to review code using this checklist, evaluate EVERY item below.
3630
+ For each failed item, provide a Reasoning Chain (see `.cursorrules` → Reasoning Clause).
3631
+ Output format:
3632
+
3633
+ ```
3634
+ ## PR REVIEW RESULTS
3635
+ ━━━━━━━━━━━━━━━━━━━
3636
+
3637
+ ✅ [Item] — Passes
3638
+ ❌ [Item] — FAILS
3639
+ 📌 Rule: [rule file + section]
3640
+ ❌ Problem: [specific issue found]
3641
+ ✅ Fix: [what to change]
3642
+
3643
+ VERDICT: PASS ✅ / FAIL ❌ (X/Y items passed)
3644
+ ```
3645
+
3646
+ ---
3647
+
3648
+ ## The Checklist
3649
+
3650
+ ### 1. Naming (→ rules/naming-conv.md)
3651
+ - [ ] All variables are descriptive nouns (no `data`, `temp`, `val`, `x`)
3652
+ - [ ] All functions start with a verb (no `userData()`, `orderLogic()`)
3653
+ - [ ] All booleans use `is/has/can/should` prefix
3654
+ - [ ] Constants use SCREAMING_SNAKE_CASE with unit suffix
3655
+ - [ ] No single-letter variables (except `i` in classic for-loops)
3656
+ - [ ] File names follow the project's chosen convention consistently
3657
+
3658
+ ### 2. Architecture (→ rules/architecture.md)
3659
+ - [ ] No layer leaks (controllers don't query DB, services don't return HTTP responses)
3660
+ - [ ] Feature-based file organization (not technical grouping)
3661
+ - [ ] Dependencies flow inward (transport → service → repository)
3662
+ - [ ] Module boundaries respected (no reaching into another module's internals)
3663
+ - [ ] Domain layer has zero external dependencies
3664
+
3665
+ ### 3. Type Safety (→ stacks/typescript.md)
3666
+ - [ ] No `any` type anywhere (use `unknown` + narrowing)
3667
+ - [ ] No `// @ts-ignore` (use `@ts-expect-error` with justification comment)
3668
+ - [ ] All function return types are explicit
3669
+ - [ ] Zod schemas validate ALL external input at boundaries
3670
+ - [ ] Types derived from Zod schemas (single source of truth)
3671
+
3672
+ ### 4. Error Handling (→ rules/error-handling.md)
3673
+ - [ ] No empty catch blocks
3674
+ - [ ] No `catch(e) { console.log(e) }` without re-throw or recovery
3675
+ - [ ] Typed error classes used (not generic `new Error('...')`)
3676
+ - [ ] Error responses don't leak internal details to clients
3677
+ - [ ] Structured logging with context (traceId, userId, action)
3678
+
3679
+ ### 5. Security (→ rules/security.md)
3680
+ - [ ] All user input validated at boundaries (Zod/schema)
3681
+ - [ ] No SQL/command string concatenation with user input
3682
+ - [ ] No secrets hardcoded in source code
3683
+ - [ ] Auth checked server-side (not client-side only)
3684
+ - [ ] Error messages don't reveal internal system details
3685
+ - [ ] CORS configured (not `*` in production)
3686
+
3687
+ ### 6. Performance (→ rules/performance.md)
3688
+ - [ ] No N+1 queries (no queries inside loops)
3689
+ - [ ] All list queries have LIMIT/pagination
3690
+ - [ ] No `SELECT *` (only needed columns)
3691
+ - [ ] No synchronous I/O in async context
3692
+ - [ ] Cache has documented invalidation strategy (if caching used)
3693
+
3694
+ ### 7. Testing (→ rules/testing.md)
3695
+ - [ ] Business logic has unit tests
3696
+ - [ ] Test names follow `should [behavior] when [condition]`
3697
+ - [ ] Tests follow AAA pattern (Arrange → Act → Assert)
3698
+ - [ ] No implementation detail testing (test behavior, not structure)
3699
+ - [ ] Test data uses factories (no copy-pasted objects)
3700
+
3701
+ ### 8. Dependencies (→ rules/efficiency-vs-hype.md)
3702
+ - [ ] No new dependencies added without justification
3703
+ - [ ] No dependency that replaces < 20 lines of code
3704
+ - [ ] New packages checked for maintenance health
3705
+ - [ ] Bundle impact considered (frontend)
3706
+
3707
+ ### 9. Git (→ rules/git-workflow.md)
3708
+ - [ ] Commit messages follow Conventional Commits
3709
+ - [ ] No `console.log` debugging statements
3710
+ - [ ] No `// TODO` without a linked issue
3711
+ - [ ] No commented-out code blocks
3712
+ - [ ] `.env` not committed
3713
+
3714
+ ### 10. Documentation
3715
+ - [ ] API endpoints have OpenAPI/Swagger documentation
3716
+ - [ ] Complex business logic has comments explaining WHY
3717
+ - [ ] Public functions/methods have JSDoc/docstrings
3718
+ - [ ] README updated if new setup steps required