@ryuenn3123/agentic-senior-core 3.0.17 → 3.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.agent-context/prompts/bootstrap-design.md +84 -94
  2. package/.agent-context/prompts/init-project.md +32 -100
  3. package/.agent-context/prompts/refactor.md +22 -44
  4. package/.agent-context/prompts/review-code.md +28 -52
  5. package/.agent-context/review-checklists/architecture-review.md +31 -62
  6. package/.agent-context/review-checklists/pr-checklist.md +74 -108
  7. package/.agent-context/rules/api-docs.md +18 -206
  8. package/.agent-context/rules/architecture.md +40 -207
  9. package/.agent-context/rules/database-design.md +10 -199
  10. package/.agent-context/rules/docker-runtime.md +5 -5
  11. package/.agent-context/rules/efficiency-vs-hype.md +11 -149
  12. package/.agent-context/rules/error-handling.md +9 -231
  13. package/.agent-context/rules/event-driven.md +17 -221
  14. package/.agent-context/rules/frontend-architecture.md +66 -119
  15. package/.agent-context/rules/git-workflow.md +1 -1
  16. package/.agent-context/rules/microservices.md +28 -161
  17. package/.agent-context/rules/naming-conv.md +8 -138
  18. package/.agent-context/rules/performance.md +9 -175
  19. package/.agent-context/rules/realtime.md +11 -44
  20. package/.agent-context/rules/security.md +11 -295
  21. package/.agent-context/rules/testing.md +9 -174
  22. package/.agent-context/state/benchmark-analysis.json +3 -3
  23. package/.agent-context/state/memory-continuity-benchmark.json +1 -1
  24. package/.agent-context/state/onboarding-report.json +71 -11
  25. package/.agents/workflows/init-project.md +7 -24
  26. package/.agents/workflows/refactor.md +7 -24
  27. package/.agents/workflows/review-code.md +7 -24
  28. package/.cursorrules +22 -21
  29. package/.gemini/instructions.md +2 -2
  30. package/.github/copilot-instructions.md +2 -2
  31. package/.instructions.md +112 -213
  32. package/.windsurfrules +22 -21
  33. package/AGENTS.md +4 -4
  34. package/CONTRIBUTING.md +13 -22
  35. package/README.md +6 -20
  36. package/lib/cli/commands/init.mjs +102 -148
  37. package/lib/cli/commands/launch.mjs +3 -3
  38. package/lib/cli/commands/optimize.mjs +14 -4
  39. package/lib/cli/commands/upgrade.mjs +25 -23
  40. package/lib/cli/compiler.mjs +96 -62
  41. package/lib/cli/constants.mjs +28 -136
  42. package/lib/cli/detector/design-evidence.mjs +189 -6
  43. package/lib/cli/detector.mjs +6 -7
  44. package/lib/cli/init-detection-flow.mjs +10 -93
  45. package/lib/cli/init-selection.mjs +2 -68
  46. package/lib/cli/project-scaffolder/constants.mjs +1 -1
  47. package/lib/cli/project-scaffolder/design-contract.mjs +438 -335
  48. package/lib/cli/project-scaffolder/discovery.mjs +36 -82
  49. package/lib/cli/project-scaffolder/prompt-builders.mjs +55 -63
  50. package/lib/cli/project-scaffolder/storage.mjs +0 -4
  51. package/lib/cli/token-optimization.mjs +1 -1
  52. package/lib/cli/utils.mjs +75 -9
  53. package/package.json +2 -2
  54. package/scripts/detection-benchmark.mjs +4 -15
  55. package/scripts/documentation-boundary-audit.mjs +9 -9
  56. package/scripts/explain-on-demand-audit.mjs +11 -11
  57. package/scripts/forbidden-content-check.mjs +9 -9
  58. package/scripts/frontend-usability-audit.mjs +57 -36
  59. package/scripts/llm-judge.mjs +1 -1
  60. package/scripts/mcp-server/constants.mjs +60 -0
  61. package/scripts/mcp-server/tool-registry.mjs +149 -0
  62. package/scripts/mcp-server/tools.mjs +446 -0
  63. package/scripts/mcp-server.mjs +23 -661
  64. package/scripts/release-gate/audit-checks.mjs +426 -0
  65. package/scripts/release-gate/constants.mjs +53 -0
  66. package/scripts/release-gate/runtime.mjs +63 -0
  67. package/scripts/release-gate/static-checks.mjs +182 -0
  68. package/scripts/release-gate.mjs +13 -794
  69. package/scripts/rules-guardian-audit.mjs +14 -13
  70. package/scripts/single-source-lazy-loading-audit.mjs +3 -3
  71. package/scripts/sync-thin-adapters.mjs +5 -5
  72. package/scripts/ui-design-judge/constants.mjs +24 -0
  73. package/scripts/ui-design-judge/design-execution-summary.mjs +259 -0
  74. package/scripts/ui-design-judge/git-input.mjs +131 -0
  75. package/scripts/ui-design-judge/prompting.mjs +73 -0
  76. package/scripts/ui-design-judge/providers.mjs +102 -0
  77. package/scripts/ui-design-judge/reporting.mjs +182 -0
  78. package/scripts/ui-design-judge/rubric-calibration.mjs +214 -0
  79. package/scripts/ui-design-judge/rubric-goldset.json +188 -0
  80. package/scripts/ui-design-judge.mjs +166 -771
  81. package/scripts/ui-rubric-calibration.mjs +35 -0
  82. package/scripts/validate/config.mjs +198 -55
  83. package/scripts/validate/coverage-checks.mjs +32 -7
  84. package/scripts/validate.mjs +8 -4
  85. package/lib/cli/architect.mjs +0 -431
@@ -1,154 +1,16 @@
1
- # Efficiency vs. Hype — Choose Stable, Not Shiny
2
-
3
- > Every dependency is a liability. Every `npm install` is a trust decision.
4
- > The best dependency is the one you don't need.
1
+ # Dependency and Tooling Boundary
5
2
 
6
3
  ## Latest-Compatible-First Rule
7
4
 
8
- - Start from the latest stable compatible dependency version, not an outdated tutorial version.
9
- - If the framework has an official scaffolder or setup command that produces current defaults, prefer that path over manually assembling stale `package.json` entries one by one.
10
- - Only step down to an older dependency version after documenting the exact compatibility, runtime, platform, or ecosystem reason.
11
- - Treat release notes, official docs, and framework migration guides as the primary source of truth for dependency setup.
12
-
13
- ## The Dependency Decision Framework
14
-
15
- Before adding ANY dependency, answer these 5 questions:
16
-
17
- ### 1. Can You Do It Without a Library? (The stdlib-first Rule)
18
- ```
19
- ❌ OVER-ENGINEERING:
20
- npm install is-odd # 1 line of code: n % 2 !== 0
21
- npm install left-pad # Already in String.prototype.padStart
22
- npm install is-number # typeof x === 'number' && !isNaN(x)
23
- npm install array-flatten # Array.prototype.flat() exists since ES2019
24
-
25
- ✅ USE THE LANGUAGE:
26
- const isOdd = (n: number) => n % 2 !== 0;
27
- const padded = str.padStart(10, '0');
28
- const flat = nested.flat(Infinity);
29
- ```
30
-
31
- **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, why the standard library is insufficient, and why the chosen version is the latest stable compatible option.
32
-
33
- ### 2. Is It Maintained? (The Pulse Check)
34
- | Signal | 🟢 Healthy | 🔴 Dead |
35
- |--------|-----------|---------|
36
- | Last commit | < 6 months ago | > 2 years ago |
37
- | Open issues | Actively triaged | 500+ unread |
38
- | Downloads/week | Growing or stable | Declining |
39
- | Bus factor | > 3 maintainers | 1 maintainer, inactive |
40
- | Security | No known CVEs | Unpatched vulnerabilities |
41
-
42
- **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.
43
-
44
- ### 3. What's the Cost? (The Weight Check)
45
- ```
46
- Before: npm install moment # 289KB minified, entire library for date formatting
47
- After: npm install date-fns # 12KB for just the functions you use (tree-shakeable)
48
- Better: Intl.DateTimeFormat # 0KB — built into the runtime
49
- ```
50
-
51
- **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.
52
-
53
- ### 4. Is It the Community Standard? (The Ecosystem Rule)
54
- Prefer packages that the ecosystem has settled on:
55
-
56
- | Need | ✅ Standard | ❌ Avoid |
57
- |------|-----------|---------|
58
- | HTTP client (Node) | `undici` (built-in) / native `fetch` / `ky` | `axios` (declining, CVE-2025-58754, bloated) |
59
- | Validation | `zod` | `joi` (heavier), `yup` (less type-safe) |
60
- | ORM (Node) | `prisma`, `drizzle` | `sequelize` (legacy API), `typeorm` (decorator hell) |
61
- | Date handling | `date-fns`, `dayjs`, `Temporal` (when stable) | `moment` (deprecated, massive) |
62
- | Testing | `vitest` (new projects), `jest` (existing) | `mocha` + `chai` + `sinon` (3 deps for what 1 does) |
63
- | Password hashing | `argon2` (OWASP primary), `bcrypt` (legacy) | Custom crypto (NEVER) |
64
- | Env loading | `dotenv` (if needed) | Custom `.env` parser |
65
-
66
- **Note:** These are current recommendations as of March 2026. Evaluate against this framework; don't blindly follow.
67
-
68
- ### 5. Can You Remove It Later? (The Exit Strategy)
69
- ```
70
- ❌ HIGH LOCK-IN:
71
- // Decorators from a specific framework scattered across 200 files
72
- @Controller() @Injectable() @Guard() // In every file — framework is your entire architecture
73
-
74
- ✅ LOW LOCK-IN:
75
- // Framework stays at the edges; business logic is framework-free
76
- // Switching from Express to Fastify only changes the transport layer
77
- ```
78
-
79
- **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.
5
+ The LLM may choose modern libraries and tooling when they fit the project. This rule does not prefer "no library" or any fixed dependency set.
80
6
 
81
- ---
82
-
83
- ## The Hype Trap (AI-Generated Code Alert)
84
-
85
- AI agents love suggesting trendy libraries because they appear frequently in training data. Watch for:
86
-
87
- ### Red Flags
88
- 1. **"Just install X"** without explaining why ASK: Can I do this with the stdlib?
89
- 2. **Library does one thing** that's 5 lines of code → REJECT: Write it yourself
90
- 3. **Library is in alpha/beta** with < 1 year of releases → REJECT: Not production-ready
91
- 4. **Library has a cooler API** but the current one works fine → REJECT: "Cool" is not a business requirement
92
- 5. **"Everyone is using it"** → SO WHAT: Popularity is not a quality signal
93
-
94
- ### The Agent Must Justify Dependencies
95
- When an AI agent suggests adding a new dependency, it MUST provide:
96
- ```
97
- 📦 DEPENDENCY JUSTIFICATION
98
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━
99
- Package: [name@version]
100
- Purpose: [what it does in 1 sentence]
101
- Stdlib Alternative: [why it's insufficient — or "none"]
102
- Bundle Size: [minified + gzipped]
103
- Maintenance: [last release date, maintainer count]
104
- Version Source: [official docs, release notes, or framework reference]
105
- Lock-in Risk: [low/medium/high — how many files would it touch]
106
- Exit Strategy: [how to remove it if needed]
107
- Fallback Reason: [only required when not using the latest stable compatible version]
108
- ```
109
-
110
- ---
111
-
112
- ## Dependency Update Strategy
113
-
114
- 1. **Start from latest stable compatible versions** — older versions need a written reason
115
- 2. **Prefer official framework setup flows** when they yield newer, canonical dependency sets
116
- 3. **Pin exact versions** in lockfiles (already default with package-lock.json, bun.lockb)
117
- 4. **Review changelogs** before major updates — never blindly `npm update`
118
- 5. **Update in isolation** — one dependency per PR, with tests passing
119
- 6. **Security patches immediately** — don't wait for the sprint
120
- 7. **Monthly audit** — run `npm audit` / `bun audit` and address findings
121
-
122
- ---
123
-
124
- ## The Zero-Dependency Ideal
125
-
126
- The best packages are those with zero or minimal dependencies themselves:
127
- - `zod` — 0 dependencies ✅
128
- - `date-fns` — 0 dependencies ✅
129
- - `nanoid` — 0 dependencies ✅
130
- - `bcrypt` — 1 native dependency (justified for crypto) ✅
131
-
132
- A package that pulls in 50 transitive dependencies for a simple feature is a supply chain attack waiting to happen.
133
-
134
- ---
135
-
136
- ## Quick Decision Tree
137
-
138
- ```
139
- Do I need this functionality?
140
- → No → Don't install anything
141
- → Yes ↓
142
-
143
- Can I write it in < 20 lines?
144
- → Yes → Write it yourself
145
- → No ↓
146
-
147
- Does the language/runtime provide it?
148
- → Yes → Use the built-in
149
- → No ↓
7
+ Before adding or recommending a dependency:
8
+ - check current official docs, release notes, and setup guidance when the ecosystem decision matters
9
+ - choose the latest stable compatible dependency version unless a project constraint blocks it
10
+ - use the official scaffolder or setup command when it creates the current supported project shape
11
+ - Only step down to an older dependency version after documenting the exact compatibility, runtime, platform, or ecosystem reason.
12
+ - explain why the dependency improves maintainability, UX, performance, security, or delivery speed
13
+ - avoid packages that are stale, thinly maintained, too heavy for the job, or added only because they are popular
14
+ - keep dependency boundaries replaceable when the library would spread through many files
150
15
 
151
- Is there a well-maintained, lightweight, community-standard package?
152
- → Yes → Install it, add justification comment
153
- → No → Build a minimal internal implementation, consider vendoring
154
- ```
16
+ Reject offline dependency decisions, outdated tutorial versions, and trend choices that are not grounded in the current repo and brief.
@@ -1,234 +1,12 @@
1
- # Error Handling — Never Swallow, Always Context
1
+ # Error Handling Boundary
2
2
 
3
- > A swallowed error is a silent production incident waiting to happen.
4
- > When it explodes at 3 AM, you won't know where to look.
3
+ Use the target language and framework's normal error model. Do not invent a custom exception architecture from this repo.
5
4
 
6
- ## The Three Commandments
5
+ Reject these failure patterns:
6
+ - swallowed errors
7
+ - generic errors that erase the domain cause
8
+ - client-facing leaks of stack traces, secrets, SQL, infrastructure details, or provider internals
9
+ - retries without transient-failure evidence, limits, backoff, and a clear final outcome
10
+ - logs that say only "something failed" without action, target, actor, or trace context
7
11
 
8
- 1. **Never swallow an error.** Every error must be logged, re-thrown, or explicitly handled.
9
- 2. **Always add context.** A stack trace is not enough — include WHAT was happening and WITH WHAT data.
10
- 3. **Fail fast at boundaries.** Validate early, reject bad data before it travels deep into the system.
11
-
12
- ---
13
-
14
- ## Swallowed Error Detection (Instant Rejection)
15
-
16
- ```
17
- ❌ DEATH PENALTY: Empty catch block
18
- try {
19
- await processPayment(order);
20
- } catch (error) {
21
- // silently swallowed — payment may have failed, user has no idea
22
- }
23
-
24
- ❌ DEATH PENALTY: Catch and log only (no re-throw or recovery)
25
- try {
26
- await processPayment(order);
27
- } catch (error) {
28
- console.log('error:', error); // Logged to a void nobody reads
29
- // Execution continues as if nothing happened
30
- }
31
-
32
- ✅ CORRECT: Handle, log with context, re-throw or recover
33
- try {
34
- await processPayment(order);
35
- } catch (error) {
36
- logger.error('Payment processing failed', {
37
- orderId: order.id,
38
- userId: order.userId,
39
- amount: order.total,
40
- error: error instanceof Error ? error.message : String(error),
41
- });
42
- throw new PaymentFailedError(order.id, { cause: error });
43
- }
44
- ```
45
-
46
- ---
47
-
48
- ## Typed Error Codes
49
-
50
- ### Rule: Use Explicit Error Types, Not Generic Errors
51
-
52
- ```
53
- ❌ BANNED: Generic errors
54
- throw new Error('Not found');
55
- throw new Error('Permission denied');
56
- throw new Error('Invalid input');
57
-
58
- ✅ REQUIRED: Typed, domain-specific errors
59
- throw new NotFoundError('User', userId);
60
- throw new ForbiddenError('Cannot delete other user\'s order');
61
- throw new ValidationError('Email format is invalid', { field: 'email' });
62
- ```
63
-
64
- ### Error Class Pattern (Any Language)
65
- ```typescript
66
- // Base application error
67
- class AppError extends Error {
68
- constructor(
69
- message: string,
70
- public readonly code: string,
71
- public readonly statusCode: number,
72
- public readonly context?: Record<string, unknown>,
73
- ) {
74
- super(message);
75
- this.name = this.constructor.name;
76
- }
77
- }
78
-
79
- // Specific errors
80
- class NotFoundError extends AppError {
81
- constructor(resource: string, id: string | number) {
82
- super(`${resource} not found: ${id}`, 'NOT_FOUND', 404, { resource, id });
83
- }
84
- }
85
-
86
- class ValidationError extends AppError {
87
- constructor(message: string, context?: Record<string, unknown>) {
88
- super(message, 'VALIDATION_ERROR', 400, context);
89
- }
90
- }
91
-
92
- class ForbiddenError extends AppError {
93
- constructor(reason: string) {
94
- super(reason, 'FORBIDDEN', 403);
95
- }
96
- }
97
- ```
98
-
99
- ---
100
-
101
- ## Structured Logging
102
-
103
- ### Rule: Logs Must Include Context
104
-
105
- ```
106
- ❌ USELESS:
107
- logger.error('Something went wrong');
108
- logger.info('Processing...');
109
- console.log(error);
110
-
111
- ✅ USEFUL:
112
- logger.error('Order processing failed', {
113
- traceId: req.traceId,
114
- userId: currentUser.id,
115
- orderId: order.id,
116
- action: 'processOrder',
117
- error: error.message,
118
- stack: error.stack,
119
- });
120
-
121
- logger.info('Payment completed', {
122
- traceId: req.traceId,
123
- orderId: order.id,
124
- amount: payment.amount,
125
- provider: payment.provider,
126
- durationMs: Date.now() - startTime,
127
- });
128
- ```
129
-
130
- ### Required Log Fields
131
- | Field | When | Purpose |
132
- |-------|------|---------|
133
- | `traceId` / `requestId` | Always (in request context) | Correlate logs across services |
134
- | `userId` | When authenticated | Who triggered the action |
135
- | `action` | Always | What was happening |
136
- | `error` + `stack` | On errors | What went wrong |
137
- | `durationMs` | On slow operations | Performance visibility |
138
-
139
- ---
140
-
141
- ## Error Response Format (APIs)
142
-
143
- ### Rule: NEVER Leak Internal Details
144
-
145
- ```
146
- ❌ BANNED response to client:
147
- {
148
- "error": "ER_NO_SUCH_TABLE: Table 'mydb.user_sessions' doesn't exist",
149
- "stack": "Error: at Query.execute (/app/node_modules/mysql..."
150
- }
151
- // Congratulations, you just told the attacker your DB name and framework
152
-
153
- ✅ REQUIRED response to client:
154
- {
155
- "error": {
156
- "code": "INTERNAL_ERROR",
157
- "message": "An unexpected error occurred. Please try again.",
158
- "traceId": "abc-123-def"
159
- }
160
- }
161
- // Internal details go to your logs, not to the client
162
- ```
163
-
164
- ### Error Response Mapping
165
- | Internal Error | HTTP Status | Client Message |
166
- |---------------|-------------|----------------|
167
- | `ValidationError` | 400 | Show specific field errors |
168
- | `AuthenticationError` | 401 | "Invalid credentials" (never specify which field) |
169
- | `ForbiddenError` | 403 | "Insufficient permissions" |
170
- | `NotFoundError` | 404 | "Resource not found" |
171
- | `ConflictError` | 409 | "Resource already exists" |
172
- | `RateLimitError` | 429 | "Too many requests" |
173
- | Unhandled errors | 500 | "Internal error" + traceId for support |
174
-
175
- ---
176
-
177
- ## Fail-Fast at Boundaries
178
-
179
- ```
180
- // ✅ Validate at the entry point, fail before going deeper
181
- async function createOrder(req: Request) {
182
- // Step 1: Validate input IMMEDIATELY
183
- const parsed = CreateOrderSchema.safeParse(req.body);
184
- if (!parsed.success) {
185
- throw new ValidationError('Invalid order data', parsed.error.flatten());
186
- }
187
-
188
- // Step 2: Now use the validated, typed data
189
- const order = await orderService.create(parsed.data);
190
- return order;
191
- }
192
-
193
- // ❌ Don't let bad data travel deep before failing
194
- async function createOrder(req: Request) {
195
- const data = req.body; // Unvalidated!
196
- const order = await orderService.create(data);
197
- // Service calls repository, repository tries to insert...
198
- // Database throws a cryptic constraint violation 5 layers deep
199
- }
200
- ```
201
-
202
- ---
203
-
204
- ## Retry Strategy
205
-
206
- When retrying failed operations:
207
-
208
- 1. **Only retry transient errors** (network timeouts, 503s) — NEVER retry validation errors or auth failures
209
- 2. **Use exponential backoff** — 100ms → 200ms → 400ms → 800ms
210
- 3. **Set a maximum retry count** (typically 3)
211
- 4. **Log every retry attempt** with attempt number and error
212
- 5. **Add jitter** to prevent thundering herd
213
-
214
- ```typescript
215
- async function withRetry<T>(
216
- operation: () => Promise<T>,
217
- maxAttempts: number = 3,
218
- baseDelayMs: number = 100,
219
- ): Promise<T> {
220
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
221
- try {
222
- return await operation();
223
- } catch (error) {
224
- if (attempt === maxAttempts || !isTransientError(error)) {
225
- throw error;
226
- }
227
- const delay = baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100;
228
- logger.warn('Retrying operation', { attempt, maxAttempts, delayMs: delay });
229
- await sleep(delay);
230
- }
231
- }
232
- throw new Error('Unreachable');
233
- }
234
- ```
12
+ At boundaries, validate early, return safe user-facing errors, and keep machine-readable error context for operators and callers.
@@ -1,226 +1,22 @@
1
- # Event-Driven Architecture — React to Facts, Don't Poll for Changes
1
+ # Event Boundary
2
2
 
3
- > If your services are constantly asking "Did anything change?", your architecture is broken.
4
- > Events are the nervous system of a distributed system.
3
+ Do not add event-driven architecture because it sounds modern. Use it only when the product or repo shows a real async boundary.
5
4
 
6
- ## When to Use Event-Driven Architecture
5
+ Use events when:
6
+ - multiple independent consumers must react to the same fact
7
+ - synchronous coupling would harm reliability, latency, or ownership
8
+ - audit history, fan-out, or eventual consistency is a real requirement
9
+ - the team can operate retries, monitoring, and failure recovery
7
10
 
8
- ### Use Events When:
9
- 1. Multiple services need to react to the same state change
10
- 2. You need temporal decoupling (producer doesn't wait for consumer)
11
- 3. Audit trail / event history is a business requirement
12
- 4. Systems need to scale producers and consumers independently
13
- 5. Eventual consistency is acceptable
11
+ Reject events when a direct call, database transaction, or simple module boundary is enough.
14
12
 
15
- ### Don't Use Events When:
16
- - You need an immediate, synchronous response
17
- - The system is a simple CRUD application with 1-2 services
18
- - You can't invest in proper infrastructure (message broker, monitoring)
19
- - Your team has no experience with async debugging
13
+ Hard rules:
14
+ - events describe facts that already happened
15
+ - payloads are versioned, typed, and documented
16
+ - producers do not know consumer internals
17
+ - consumers are idempotent
18
+ - retries are bounded and dead-letter or recovery behavior is defined
19
+ - transactional publishing uses an outbox or equivalent safety pattern when data consistency matters
20
+ - event catalogs or docs identify producer, consumers, ownership, and schema evolution rules
20
21
 
21
- ---
22
-
23
- ## Core Patterns
24
-
25
- ### 1. Pub/Sub (Publish-Subscribe)
26
-
27
- Producer emits an event. Zero or more consumers react independently.
28
-
29
- ```
30
- ┌──────────┐
31
- │ Consumer A│ (Send email)
32
- └────▲──────┘
33
- ┌──────────┐ │
34
- │ Producer │──→ EVENT BUS ──→┌──────────┐
35
- │ (Order │ │ │ Consumer B│ (Update inventory)
36
- │ Service) │ │ └──────────┘
37
- └──────────┘ │
38
- ┌───▼──────┐
39
- │ Consumer C│ (Analytics)
40
- └──────────┘
41
- ```
42
-
43
- **Rules:**
44
- - Producer does NOT know about consumers (fire-and-forget)
45
- - Each consumer processes independently (failure in one doesn't affect others)
46
- - Consumers MUST be idempotent (same event processed twice = same result)
47
-
48
- ### 2. CQRS (Command Query Responsibility Segregation)
49
-
50
- Separate the write model (commands) from the read model (queries).
51
-
52
- ```
53
- ┌─────────────┐ ┌─────────────┐
54
- │ Write Side │ Events │ Read Side │
55
- │ (Commands) │ ──────────────→ │ (Queries) │
56
- │ │ │ │
57
- │ Rich domain │ │ Denormalized │
58
- │ model │ │ read models │
59
- │ Normalized │ │ Optimized │
60
- │ schema │ │ for queries │
61
- └─────────────┘ └─────────────┘
62
- ```
63
-
64
- **When to use CQRS:**
65
- - Read/write ratio is heavily skewed (100:1 reads to writes)
66
- - Read and write models have fundamentally different shapes
67
- - You need different scaling for reads vs writes
68
-
69
- **When NOT to use CQRS:**
70
- - Simple CRUD with no complex queries
71
- - Read and write models are identical (just use a repository)
72
- - You don't have the team capacity to maintain two models
73
-
74
- ### 3. Event Sourcing
75
-
76
- Store the full history of state changes as immutable events, not just the current state.
77
-
78
- ```
79
- Traditional: User { name: "Jane", email: "jane@new.com" }
80
- → You only know the current state
81
-
82
- Event Sourced:
83
- 1. UserCreated { name: "Jane", email: "jane@old.com" }
84
- 2. EmailChanged { email: "jane@mid.com" }
85
- 3. EmailChanged { email: "jane@new.com" }
86
- → You know the full history, can rebuild any point in time
87
- ```
88
-
89
- **When to use Event Sourcing:**
90
- - Audit trail is a regulatory requirement (finance, healthcare)
91
- - "Time travel" queries are needed (what was the state on March 1?)
92
- - Complex domain with many state transitions
93
- - Combined with CQRS for complex read requirements
94
-
95
- **When NOT to use Event Sourcing:**
96
- - Simple CRUD applications (overkill)
97
- - Team has no experience with event stores
98
- - No clear business need for historical state
99
-
100
- ---
101
-
102
- ## Event Design Rules
103
-
104
- ### Naming: Past Tense, Domain Language
105
-
106
- ```
107
- ❌ BANNED:
108
- "UpdateOrder" → Commands, not events
109
- "ORDER_UPDATE" → Screaming snake in an event name
110
- "data" → Meaningless
111
-
112
- ✅ REQUIRED:
113
- "OrderPlaced" → Past tense, describes what happened
114
- "PaymentProcessed" → Specific, clear domain action
115
- "InventoryReserved" → Business language, not technical
116
- ```
117
-
118
- ### Event Schema: Include Context
119
-
120
- ```typescript
121
- // ❌ BANNED: Minimal event with no context
122
- { type: "OrderPlaced", orderId: "123" }
123
-
124
- // ✅ REQUIRED: Rich event with all necessary context
125
- {
126
- eventId: "evt_abc123", // Unique, for idempotency
127
- eventType: "OrderPlaced", // What happened
128
- aggregateId: "order_123", // Which entity
129
- aggregateType: "Order", // Entity type
130
- timestamp: "2026-03-11T...", // When it happened
131
- version: 1, // Schema version
132
- correlationId: "req_xyz", // Trace across services
133
- causationId: "cmd_456", // What caused this event
134
- data: { // The event payload
135
- orderId: "order_123",
136
- userId: "user_789",
137
- items: [...],
138
- totalAmount: 99.99,
139
- currency: "USD"
140
- },
141
- metadata: { // Operational metadata
142
- producerService: "order-service",
143
- producerVersion: "2.1.0"
144
- }
145
- }
146
- ```
147
-
148
- ### Event Versioning
149
-
150
- Events are immutable — once published, they can't change. Handle evolution with:
151
-
152
- 1. **Weak schema (preferred):** Consumers ignore unknown fields, use defaults for missing fields
153
- 2. **Upcasting:** Transform old events to new schema on read
154
- 3. **New event type:** If the change is breaking, create `OrderPlacedV2`
155
-
156
- ```
157
- BANNED: Changing the schema of an existing event in a breaking way
158
- BANNED: Removing fields from events
159
- ALLOWED: Adding optional fields with defaults
160
- ALLOWED: Creating a new event type for breaking changes
161
- ```
162
-
163
- ---
164
-
165
- ## Infrastructure Choices
166
-
167
- | Need | Recommended | Avoid |
168
- |------|-----------|-------|
169
- | General pub/sub | Apache Kafka, NATS, RabbitMQ | Custom TCP sockets |
170
- | Cloud-native | AWS EventBridge, GCP Pub/Sub, Azure Service Bus | Polling a database table |
171
- | Simple/local | Redis Streams, NATS | ZeroMQ for production events |
172
- | Event store | EventStoreDB, Kafka (with compaction) | Relational DB as event store (unless simple) |
173
-
174
- ---
175
-
176
- ## Reliability Patterns
177
-
178
- ### Outbox Pattern (Transactional Events)
179
-
180
- Ensure events are published reliably alongside database writes:
181
-
182
- ```
183
- 1. Write to database AND outbox table in a single transaction
184
- 2. Background process reads outbox and publishes to message broker
185
- 3. Mark outbox entry as published
186
-
187
- This guarantees: if the DB write succeeds, the event WILL be published.
188
- No more "DB updated but event lost" bugs.
189
- ```
190
-
191
- ### Dead Letter Queue (DLQ)
192
-
193
- Messages that fail processing after N retries go to a DLQ:
194
- - Monitor DLQ size — it should be near zero
195
- - Alert when DLQ grows
196
- - Investigate and reprocess failed messages
197
- - Never ignore a growing DLQ
198
-
199
- ### Idempotency (Non-Negotiable)
200
-
201
- ```
202
- EVERY consumer MUST handle duplicate events safely.
203
-
204
- Techniques:
205
- 1. Idempotency key: Store processed eventIds, skip if seen
206
- 2. Natural idempotency: Operations that are naturally safe to repeat
207
- (e.g., SET status = 'paid' is idempotent; INCREMENT balance is NOT)
208
- 3. Optimistic locking: Use version numbers to detect conflicts
209
- ```
210
-
211
- ---
212
-
213
- ## The Event-Driven Checklist
214
-
215
- Before implementing event-driven patterns:
216
-
217
- - [ ] Business justification exists (not just "it's modern")
218
- - [ ] Team understands eventual consistency trade-offs
219
- - [ ] Message broker selected and provisioned
220
- - [ ] Event schema defined with versioning strategy
221
- - [ ] All consumers are idempotent
222
- - [ ] Dead letter queue configured with monitoring
223
- - [ ] Distributed tracing in place (OpenTelemetry)
224
- - [ ] Outbox pattern used for transactional events (if needed)
225
- - [ ] Consumer failure handling defined (retry, DLQ, alert)
226
- - [ ] Event catalog maintained (what events exist, who produces/consumes)
22
+ If event tooling is unresolved, the LLM must recommend a current project-fit broker or managed service from official docs before implementation.