opencode-metis 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +140 -0
  2. package/dist/cli.cjs +63 -0
  3. package/dist/mcp-server.cjs +51 -0
  4. package/dist/plugin.cjs +4 -0
  5. package/dist/worker.cjs +224 -0
  6. package/opencode/agent/the-analyst/feature-prioritization.md +66 -0
  7. package/opencode/agent/the-analyst/market-research.md +77 -0
  8. package/opencode/agent/the-analyst/project-coordination.md +81 -0
  9. package/opencode/agent/the-analyst/requirements-analysis.md +77 -0
  10. package/opencode/agent/the-architect/compatibility-review.md +138 -0
  11. package/opencode/agent/the-architect/complexity-review.md +137 -0
  12. package/opencode/agent/the-architect/quality-review.md +67 -0
  13. package/opencode/agent/the-architect/security-review.md +127 -0
  14. package/opencode/agent/the-architect/system-architecture.md +119 -0
  15. package/opencode/agent/the-architect/system-documentation.md +83 -0
  16. package/opencode/agent/the-architect/technology-research.md +85 -0
  17. package/opencode/agent/the-chief.md +79 -0
  18. package/opencode/agent/the-designer/accessibility-implementation.md +101 -0
  19. package/opencode/agent/the-designer/design-foundation.md +74 -0
  20. package/opencode/agent/the-designer/interaction-architecture.md +75 -0
  21. package/opencode/agent/the-designer/user-research.md +70 -0
  22. package/opencode/agent/the-meta-agent.md +155 -0
  23. package/opencode/agent/the-platform-engineer/ci-cd-pipelines.md +109 -0
  24. package/opencode/agent/the-platform-engineer/containerization.md +106 -0
  25. package/opencode/agent/the-platform-engineer/data-architecture.md +81 -0
  26. package/opencode/agent/the-platform-engineer/dependency-review.md +144 -0
  27. package/opencode/agent/the-platform-engineer/deployment-automation.md +81 -0
  28. package/opencode/agent/the-platform-engineer/infrastructure-as-code.md +107 -0
  29. package/opencode/agent/the-platform-engineer/performance-tuning.md +82 -0
  30. package/opencode/agent/the-platform-engineer/pipeline-engineering.md +81 -0
  31. package/opencode/agent/the-platform-engineer/production-monitoring.md +105 -0
  32. package/opencode/agent/the-qa-engineer/exploratory-testing.md +66 -0
  33. package/opencode/agent/the-qa-engineer/performance-testing.md +81 -0
  34. package/opencode/agent/the-qa-engineer/quality-assurance.md +77 -0
  35. package/opencode/agent/the-qa-engineer/test-execution.md +66 -0
  36. package/opencode/agent/the-software-engineer/api-development.md +78 -0
  37. package/opencode/agent/the-software-engineer/component-development.md +79 -0
  38. package/opencode/agent/the-software-engineer/concurrency-review.md +141 -0
  39. package/opencode/agent/the-software-engineer/domain-modeling.md +66 -0
  40. package/opencode/agent/the-software-engineer/performance-optimization.md +113 -0
  41. package/opencode/command/analyze.md +149 -0
  42. package/opencode/command/constitution.md +178 -0
  43. package/opencode/command/debug.md +194 -0
  44. package/opencode/command/document.md +178 -0
  45. package/opencode/command/implement.md +225 -0
  46. package/opencode/command/refactor.md +207 -0
  47. package/opencode/command/review.md +229 -0
  48. package/opencode/command/simplify.md +267 -0
  49. package/opencode/command/specify.md +191 -0
  50. package/opencode/command/validate.md +224 -0
  51. package/opencode/skill/accessibility-design/SKILL.md +566 -0
  52. package/opencode/skill/accessibility-design/checklists/wcag-checklist.md +435 -0
  53. package/opencode/skill/agent-coordination/SKILL.md +224 -0
  54. package/opencode/skill/api-contract-design/SKILL.md +550 -0
  55. package/opencode/skill/api-contract-design/templates/graphql-schema-template.md +818 -0
  56. package/opencode/skill/api-contract-design/templates/rest-api-template.md +417 -0
  57. package/opencode/skill/architecture-design/SKILL.md +160 -0
  58. package/opencode/skill/architecture-design/examples/architecture-examples.md +170 -0
  59. package/opencode/skill/architecture-design/template.md +749 -0
  60. package/opencode/skill/architecture-design/validation.md +99 -0
  61. package/opencode/skill/architecture-selection/SKILL.md +522 -0
  62. package/opencode/skill/architecture-selection/examples/adrs/001-example-adr.md +71 -0
  63. package/opencode/skill/architecture-selection/examples/architecture-patterns.md +239 -0
  64. package/opencode/skill/bug-diagnosis/SKILL.md +235 -0
  65. package/opencode/skill/code-quality-review/SKILL.md +337 -0
  66. package/opencode/skill/code-quality-review/examples/anti-patterns.md +629 -0
  67. package/opencode/skill/code-quality-review/reference.md +322 -0
  68. package/opencode/skill/code-review/SKILL.md +363 -0
  69. package/opencode/skill/code-review/reference.md +450 -0
  70. package/opencode/skill/codebase-analysis/SKILL.md +139 -0
  71. package/opencode/skill/codebase-navigation/SKILL.md +227 -0
  72. package/opencode/skill/codebase-navigation/examples/exploration-patterns.md +263 -0
  73. package/opencode/skill/coding-conventions/SKILL.md +178 -0
  74. package/opencode/skill/coding-conventions/checklists/accessibility-checklist.md +176 -0
  75. package/opencode/skill/coding-conventions/checklists/performance-checklist.md +154 -0
  76. package/opencode/skill/coding-conventions/checklists/security-checklist.md +127 -0
  77. package/opencode/skill/constitution-validation/SKILL.md +315 -0
  78. package/opencode/skill/constitution-validation/examples/CONSTITUTION.md +202 -0
  79. package/opencode/skill/constitution-validation/reference/rule-patterns.md +328 -0
  80. package/opencode/skill/constitution-validation/template.md +115 -0
  81. package/opencode/skill/context-preservation/SKILL.md +445 -0
  82. package/opencode/skill/data-modeling/SKILL.md +385 -0
  83. package/opencode/skill/data-modeling/templates/schema-design-template.md +268 -0
  84. package/opencode/skill/deployment-pipeline-design/SKILL.md +579 -0
  85. package/opencode/skill/deployment-pipeline-design/templates/pipeline-template.md +633 -0
  86. package/opencode/skill/documentation-extraction/SKILL.md +259 -0
  87. package/opencode/skill/documentation-sync/SKILL.md +431 -0
  88. package/opencode/skill/domain-driven-design/SKILL.md +509 -0
  89. package/opencode/skill/domain-driven-design/examples/ddd-patterns.md +688 -0
  90. package/opencode/skill/domain-driven-design/reference.md +465 -0
  91. package/opencode/skill/drift-detection/SKILL.md +383 -0
  92. package/opencode/skill/drift-detection/reference.md +340 -0
  93. package/opencode/skill/error-recovery/SKILL.md +162 -0
  94. package/opencode/skill/error-recovery/examples/error-patterns.md +484 -0
  95. package/opencode/skill/feature-prioritization/SKILL.md +419 -0
  96. package/opencode/skill/feature-prioritization/examples/rice-template.md +139 -0
  97. package/opencode/skill/feature-prioritization/reference.md +256 -0
  98. package/opencode/skill/git-workflow/SKILL.md +453 -0
  99. package/opencode/skill/implementation-planning/SKILL.md +215 -0
  100. package/opencode/skill/implementation-planning/examples/phase-examples.md +217 -0
  101. package/opencode/skill/implementation-planning/template.md +220 -0
  102. package/opencode/skill/implementation-planning/validation.md +88 -0
  103. package/opencode/skill/implementation-verification/SKILL.md +272 -0
  104. package/opencode/skill/knowledge-capture/SKILL.md +265 -0
  105. package/opencode/skill/knowledge-capture/reference/knowledge-capture.md +402 -0
  106. package/opencode/skill/knowledge-capture/reference.md +444 -0
  107. package/opencode/skill/knowledge-capture/templates/domain-template.md +325 -0
  108. package/opencode/skill/knowledge-capture/templates/interface-template.md +255 -0
  109. package/opencode/skill/knowledge-capture/templates/pattern-template.md +144 -0
  110. package/opencode/skill/observability-design/SKILL.md +291 -0
  111. package/opencode/skill/observability-design/references/monitoring-patterns.md +461 -0
  112. package/opencode/skill/pattern-detection/SKILL.md +171 -0
  113. package/opencode/skill/pattern-detection/examples/common-patterns.md +359 -0
  114. package/opencode/skill/performance-analysis/SKILL.md +266 -0
  115. package/opencode/skill/performance-analysis/references/profiling-tools.md +499 -0
  116. package/opencode/skill/requirements-analysis/SKILL.md +139 -0
  117. package/opencode/skill/requirements-analysis/examples/good-prd.md +66 -0
  118. package/opencode/skill/requirements-analysis/template.md +177 -0
  119. package/opencode/skill/requirements-analysis/validation.md +69 -0
  120. package/opencode/skill/requirements-elicitation/SKILL.md +518 -0
  121. package/opencode/skill/requirements-elicitation/examples/interview-questions.md +226 -0
  122. package/opencode/skill/requirements-elicitation/examples/user-stories.md +414 -0
  123. package/opencode/skill/safe-refactoring/SKILL.md +312 -0
  124. package/opencode/skill/safe-refactoring/reference/code-smells.md +347 -0
  125. package/opencode/skill/security-assessment/SKILL.md +421 -0
  126. package/opencode/skill/security-assessment/checklists/security-review-checklist.md +285 -0
  127. package/opencode/skill/specification-management/SKILL.md +143 -0
  128. package/opencode/skill/specification-management/readme-template.md +32 -0
  129. package/opencode/skill/specification-management/reference.md +115 -0
  130. package/opencode/skill/specification-management/spec.py +229 -0
  131. package/opencode/skill/specification-validation/SKILL.md +397 -0
  132. package/opencode/skill/specification-validation/reference/3cs-framework.md +306 -0
  133. package/opencode/skill/specification-validation/reference/ambiguity-detection.md +132 -0
  134. package/opencode/skill/specification-validation/reference/constitution-validation.md +301 -0
  135. package/opencode/skill/specification-validation/reference/drift-detection.md +383 -0
  136. package/opencode/skill/task-delegation/SKILL.md +607 -0
  137. package/opencode/skill/task-delegation/examples/file-coordination.md +495 -0
  138. package/opencode/skill/task-delegation/examples/parallel-research.md +337 -0
  139. package/opencode/skill/task-delegation/examples/sequential-build.md +504 -0
  140. package/opencode/skill/task-delegation/reference.md +825 -0
  141. package/opencode/skill/tech-stack-detection/SKILL.md +89 -0
  142. package/opencode/skill/tech-stack-detection/references/framework-signatures.md +598 -0
  143. package/opencode/skill/technical-writing/SKILL.md +190 -0
  144. package/opencode/skill/technical-writing/templates/adr-template.md +205 -0
  145. package/opencode/skill/technical-writing/templates/system-doc-template.md +380 -0
  146. package/opencode/skill/test-design/SKILL.md +464 -0
  147. package/opencode/skill/test-design/examples/test-pyramid.md +724 -0
  148. package/opencode/skill/testing/SKILL.md +213 -0
  149. package/opencode/skill/testing/examples/test-pyramid.md +724 -0
  150. package/opencode/skill/user-insight-synthesis/SKILL.md +576 -0
  151. package/opencode/skill/user-insight-synthesis/templates/research-plan-template.md +217 -0
  152. package/opencode/skill/user-research/SKILL.md +508 -0
  153. package/opencode/skill/user-research/examples/interview-questions.md +265 -0
  154. package/opencode/skill/user-research/examples/personas.md +267 -0
  155. package/opencode/skill/vibe-security/SKILL.md +654 -0
  156. package/package.json +45 -0
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: error-recovery
3
+ description: Consistent error patterns, validation approaches, and recovery strategies. Use when implementing input validation, designing error responses, handling failures gracefully, or establishing logging practices. Covers operational vs programmer errors, user-facing vs internal errors, and recovery mechanisms.
4
+ license: MIT
5
+ compatibility: opencode
6
+ metadata:
7
+ category: development
8
+ version: "1.0"
9
+ ---
10
+
11
+ # Error Handling
12
+
13
+ Roleplay as an error handling specialist that designs consistent error patterns, validation approaches, and recovery strategies for robust systems.
14
+
15
+ ErrorRecovery {
16
+ Activation {
17
+ When implementing input validation at system boundaries
18
+ When designing error responses for APIs or user interfaces
19
+ When building recovery mechanisms for transient failures
20
+ When establishing logging and monitoring patterns
21
+ When distinguishing between errors that need user action vs system intervention
22
+ }
23
+
24
+ Constraints {
25
+ Errors are not exceptional - they are expected
26
+ Good error handling treats errors as first-class citizens of system design
27
+ Fail safely, provide actionable feedback, and enable recovery
28
+ Fail fast on programmer errors - do not mask bugs
29
+ Handle operational errors gracefully with recovery options
30
+ }
31
+
32
+ ErrorClassification {
33
+ OperationalErrors {
34
+ Runtime problems that occur during normal operation
35
+ These are expected and must be handled
36
+
37
+ Characteristics:
38
+ - External system failures (network, database, filesystem)
39
+ - Invalid user input
40
+ - Resource exhaustion (memory, disk, connections)
41
+ - Timeout conditions
42
+ - Rate limiting
43
+
44
+ Response: Handle gracefully, log appropriately, provide user feedback, implement recovery
45
+ }
46
+
47
+ ProgrammerErrors {
48
+ Bugs in the code that should not happen if the code is correct
49
+
50
+ Characteristics:
51
+ - Type errors caught at runtime
52
+ - Null/undefined access on required values
53
+ - Failed assertions on invariants
54
+ - Invalid internal state
55
+
56
+ Response: Fail fast, log full context, alert developers. Do not attempt recovery - fix the bug
57
+ }
58
+ }
59
+
60
+ CorePatterns {
61
+ InputValidation {
62
+ Validate early, validate completely, provide specific feedback
63
+
64
+ FailFastValidation {
65
+ 1. Validate at system boundaries (API entry, user input, file reads)
66
+ 2. Check all constraints before processing
67
+ 3. Return ALL validation errors, not just the first one
68
+ 4. Include field name, actual value (if safe), and expected format
69
+ 5. Never trust data from external sources
70
+ }
71
+
72
+ ValidationChecklist:
73
+ - Required fields present
74
+ - Types correct
75
+ - Values within allowed ranges
76
+ - Formats match expectations (email, URL, date)
77
+ - Business rules satisfied
78
+ }
79
+
80
+ ErrorMessages {
81
+ Different audiences need different information
82
+
83
+ UserFacingErrors:
84
+ - Clear action the user can take
85
+ - No technical jargon or stack traces
86
+ - Consistent tone and format
87
+ - Localization-ready
88
+
89
+ InternalLoggedErrors:
90
+ - Full technical context
91
+ - Request/correlation IDs
92
+ - Timestamp and service identifier
93
+ - Stack trace for programmer errors
94
+ - Sanitized sensitive data
95
+ }
96
+
97
+ RecoveryStrategies {
98
+ RetryWithBackoff {
99
+ For transient failures (network timeouts, rate limits)
100
+ - Exponential backoff with jitter
101
+ - Maximum retry count with circuit breaker
102
+ }
103
+
104
+ Fallback {
105
+ - Degraded functionality over complete failure
106
+ - Cached data when live data unavailable
107
+ - Default values when configuration missing
108
+ }
109
+
110
+ Compensation {
111
+ - Undo partial operations on failure
112
+ - Maintain consistency in distributed operations
113
+ - Saga pattern for multi-step processes
114
+ }
115
+ }
116
+
117
+ LoggingLevels {
118
+ | Level | Use For |
119
+ |-------|---------|
120
+ | ERROR | Operational errors requiring attention |
121
+ | WARN | Recoverable issues, degraded performance |
122
+ | INFO | Significant state changes, request lifecycle |
123
+ | DEBUG | Detailed flow for troubleshooting |
124
+
125
+ WhatToLog:
126
+ - Correlation/request ID
127
+ - User context (sanitized)
128
+ - Operation being attempted
129
+ - Error type and message
130
+ - Duration and timing
131
+
132
+ WhatNOTToLog:
133
+ - Passwords, tokens, secrets
134
+ - Full credit card numbers
135
+ - Personal identifiable information (PII)
136
+ - Raw request/response bodies containing sensitive data
137
+ }
138
+ }
139
+
140
+ BestPractices {
141
+ - Fail fast on programmer errors - do not mask bugs
142
+ - Handle operational errors gracefully with recovery options
143
+ - Provide correlation IDs for tracing requests across services
144
+ - Use structured logging (JSON) for machine parseability
145
+ - Centralize error handling logic - avoid scattered try/catch blocks
146
+ - Test error paths as rigorously as success paths
147
+ - Monitor error rates and set alerts for anomalies
148
+ }
149
+
150
+ AntiPatterns {
151
+ - Catching all exceptions silently (`catch {}`)
152
+ - Logging sensitive data in error messages
153
+ - Returning generic "Something went wrong" without context
154
+ - Retrying non-idempotent operations without safeguards
155
+ - Mixing validation errors with system errors in responses
156
+ - Treating all errors the same regardless of recoverability
157
+ }
158
+ }
159
+
160
+ ## References
161
+
162
+ - [examples/error-patterns.md](examples/error-patterns.md) - Concrete examples across languages
@@ -0,0 +1,484 @@
1
+ # Error Patterns Examples
2
+
3
+ Concrete examples demonstrating error handling patterns. Language-agnostic principles with syntax examples.
4
+
5
+ ---
6
+
7
+ ## Input Validation
8
+
9
+ ### Context
10
+
11
+ Validate at system boundaries before processing. Return all errors, not just the first one.
12
+
13
+ ### Pattern: Collect All Validation Errors
14
+
15
+ ```typescript
16
+ // TypeScript example
17
+ interface ValidationError {
18
+ field: string;
19
+ message: string;
20
+ code: string;
21
+ }
22
+
23
+ function validateUserInput(input: unknown): ValidationError[] {
24
+ const errors: ValidationError[] = [];
25
+
26
+ if (!input || typeof input !== 'object') {
27
+ return [{ field: 'root', message: 'Input must be an object', code: 'INVALID_TYPE' }];
28
+ }
29
+
30
+ const data = input as Record<string, unknown>;
31
+
32
+ if (!data.email) {
33
+ errors.push({ field: 'email', message: 'Email is required', code: 'REQUIRED' });
34
+ } else if (!isValidEmail(data.email)) {
35
+ errors.push({ field: 'email', message: 'Email format is invalid', code: 'INVALID_FORMAT' });
36
+ }
37
+
38
+ if (!data.age) {
39
+ errors.push({ field: 'age', message: 'Age is required', code: 'REQUIRED' });
40
+ } else if (typeof data.age !== 'number' || data.age < 0 || data.age > 150) {
41
+ errors.push({ field: 'age', message: 'Age must be between 0 and 150', code: 'OUT_OF_RANGE' });
42
+ }
43
+
44
+ return errors;
45
+ }
46
+ ```
47
+
48
+ ```python
49
+ # Python example
50
+ from dataclasses import dataclass
51
+ from typing import List, Any, Dict
52
+
53
+ @dataclass
54
+ class ValidationError:
55
+ field: str
56
+ message: str
57
+ code: str
58
+
59
+ def validate_user_input(data: Any) -> List[ValidationError]:
60
+ errors = []
61
+
62
+ if not isinstance(data, dict):
63
+ return [ValidationError('root', 'Input must be a dictionary', 'INVALID_TYPE')]
64
+
65
+ if not data.get('email'):
66
+ errors.append(ValidationError('email', 'Email is required', 'REQUIRED'))
67
+ elif not is_valid_email(data['email']):
68
+ errors.append(ValidationError('email', 'Email format is invalid', 'INVALID_FORMAT'))
69
+
70
+ if 'age' not in data:
71
+ errors.append(ValidationError('age', 'Age is required', 'REQUIRED'))
72
+ elif not isinstance(data['age'], int) or not 0 <= data['age'] <= 150:
73
+ errors.append(ValidationError('age', 'Age must be between 0 and 150', 'OUT_OF_RANGE'))
74
+
75
+ return errors
76
+ ```
77
+
78
+ ### Explanation
79
+
80
+ 1. Check the most fundamental constraint first (is it an object/dict?)
81
+ 2. For each field, check required before format/range
82
+ 3. Collect all errors into a list
83
+ 4. Return the complete list so users can fix all issues at once
84
+
85
+ ### Anti-Pattern
86
+
87
+ ```typescript
88
+ // BAD: Throws on first error, user must fix one at a time
89
+ function validateBad(data: any): void {
90
+ if (!data.email) throw new Error('Email required');
91
+ if (!data.age) throw new Error('Age required');
92
+ }
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Error Types and Custom Errors
98
+
99
+ ### Context
100
+
101
+ Distinguish error types to enable appropriate handling at different layers.
102
+
103
+ ### Pattern: Error Hierarchy
104
+
105
+ ```typescript
106
+ // Base error with shared properties
107
+ class AppError extends Error {
108
+ constructor(
109
+ message: string,
110
+ public readonly code: string,
111
+ public readonly isOperational: boolean,
112
+ public readonly context?: Record<string, unknown>
113
+ ) {
114
+ super(message);
115
+ this.name = this.constructor.name;
116
+ }
117
+ }
118
+
119
+ // Operational errors - expected, handle gracefully
120
+ class ValidationError extends AppError {
121
+ constructor(message: string, context?: Record<string, unknown>) {
122
+ super(message, 'VALIDATION_ERROR', true, context);
123
+ }
124
+ }
125
+
126
+ class NotFoundError extends AppError {
127
+ constructor(resource: string, id: string) {
128
+ super(`${resource} not found: ${id}`, 'NOT_FOUND', true, { resource, id });
129
+ }
130
+ }
131
+
132
+ class ExternalServiceError extends AppError {
133
+ constructor(service: string, cause?: Error) {
134
+ super(`External service failure: ${service}`, 'EXTERNAL_SERVICE_ERROR', true, {
135
+ service,
136
+ cause: cause?.message
137
+ });
138
+ }
139
+ }
140
+
141
+ // Programmer errors - unexpected, fail fast
142
+ class InvariantViolation extends AppError {
143
+ constructor(invariant: string) {
144
+ super(`Invariant violated: ${invariant}`, 'INVARIANT_VIOLATION', false);
145
+ }
146
+ }
147
+ ```
148
+
149
+ ```python
150
+ # Python example
151
+ class AppError(Exception):
152
+ def __init__(self, message: str, code: str, is_operational: bool, context: dict = None):
153
+ super().__init__(message)
154
+ self.code = code
155
+ self.is_operational = is_operational
156
+ self.context = context or {}
157
+
158
+ class ValidationError(AppError):
159
+ def __init__(self, message: str, context: dict = None):
160
+ super().__init__(message, 'VALIDATION_ERROR', True, context)
161
+
162
+ class NotFoundError(AppError):
163
+ def __init__(self, resource: str, resource_id: str):
164
+ super().__init__(
165
+ f'{resource} not found: {resource_id}',
166
+ 'NOT_FOUND',
167
+ True,
168
+ {'resource': resource, 'id': resource_id}
169
+ )
170
+ ```
171
+
172
+ ### Explanation
173
+
174
+ 1. Base error carries common properties: code, operational flag, context
175
+ 2. `isOperational` distinguishes expected errors from bugs
176
+ 3. Context provides structured data for logging without string parsing
177
+ 4. Specific error types enable pattern matching in handlers
178
+
179
+ ---
180
+
181
+ ## Recovery Strategies
182
+
183
+ ### Context
184
+
185
+ Implement retry with exponential backoff for transient failures.
186
+
187
+ ### Pattern: Retry with Backoff
188
+
189
+ ```typescript
190
+ interface RetryConfig {
191
+ maxAttempts: number;
192
+ baseDelayMs: number;
193
+ maxDelayMs: number;
194
+ }
195
+
196
+ async function withRetry<T>(
197
+ operation: () => Promise<T>,
198
+ config: RetryConfig,
199
+ isRetryable: (error: unknown) => boolean
200
+ ): Promise<T> {
201
+ let lastError: unknown;
202
+
203
+ for (let attempt = 1; attempt <= config.maxAttempts; attempt++) {
204
+ try {
205
+ return await operation();
206
+ } catch (error) {
207
+ lastError = error;
208
+
209
+ if (!isRetryable(error) || attempt === config.maxAttempts) {
210
+ throw error;
211
+ }
212
+
213
+ // Exponential backoff with jitter
214
+ const delay = Math.min(
215
+ config.baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 100,
216
+ config.maxDelayMs
217
+ );
218
+
219
+ await sleep(delay);
220
+ }
221
+ }
222
+
223
+ throw lastError;
224
+ }
225
+
226
+ // Usage
227
+ const result = await withRetry(
228
+ () => fetchFromExternalAPI(url),
229
+ { maxAttempts: 3, baseDelayMs: 100, maxDelayMs: 5000 },
230
+ (error) => error instanceof ExternalServiceError
231
+ );
232
+ ```
233
+
234
+ ### Explanation
235
+
236
+ 1. Accept retry configuration as parameter for flexibility
237
+ 2. Predicate function determines which errors are retryable
238
+ 3. Exponential backoff: 100ms, 200ms, 400ms...
239
+ 4. Jitter prevents thundering herd when many clients retry
240
+ 5. Max delay caps the wait time
241
+ 6. Re-throw on final attempt or non-retryable errors
242
+
243
+ ### Variations
244
+
245
+ - **Circuit breaker:** Track failure rate, stop retrying when threshold exceeded
246
+ - **Fallback:** Return cached/default value instead of retrying
247
+ - **Hedged requests:** Start second request before first times out
248
+
249
+ ---
250
+
251
+ ## User-Facing vs Internal Errors
252
+
253
+ ### Context
254
+
255
+ Users need actionable guidance. Logs need technical detail.
256
+
257
+ ### Pattern: Error Response Transformation
258
+
259
+ ```typescript
260
+ interface UserErrorResponse {
261
+ message: string;
262
+ code: string;
263
+ requestId: string;
264
+ }
265
+
266
+ interface LogEntry {
267
+ timestamp: string;
268
+ requestId: string;
269
+ userId?: string;
270
+ error: {
271
+ name: string;
272
+ message: string;
273
+ code: string;
274
+ stack?: string;
275
+ };
276
+ context: Record<string, unknown>;
277
+ }
278
+
279
+ function handleError(
280
+ error: unknown,
281
+ requestId: string,
282
+ userId?: string
283
+ ): { response: UserErrorResponse; logEntry: LogEntry } {
284
+ const timestamp = new Date().toISOString();
285
+
286
+ // Determine if error is safe to expose
287
+ const isOperational = error instanceof AppError && error.isOperational;
288
+
289
+ // User gets sanitized message
290
+ const response: UserErrorResponse = {
291
+ message: isOperational
292
+ ? (error as AppError).message
293
+ : 'An unexpected error occurred. Please try again.',
294
+ code: isOperational
295
+ ? (error as AppError).code
296
+ : 'INTERNAL_ERROR',
297
+ requestId
298
+ };
299
+
300
+ // Logs get full context
301
+ const logEntry: LogEntry = {
302
+ timestamp,
303
+ requestId,
304
+ userId,
305
+ error: {
306
+ name: error instanceof Error ? error.name : 'UnknownError',
307
+ message: error instanceof Error ? error.message : String(error),
308
+ code: error instanceof AppError ? error.code : 'UNKNOWN',
309
+ stack: error instanceof Error ? error.stack : undefined
310
+ },
311
+ context: error instanceof AppError ? error.context ?? {} : {}
312
+ };
313
+
314
+ return { response, logEntry };
315
+ }
316
+ ```
317
+
318
+ ### Explanation
319
+
320
+ 1. Request ID links user error to log entry for support
321
+ 2. Operational errors: safe to show message to user
322
+ 3. Programmer errors: generic message, full details in logs
323
+ 4. Never expose stack traces to users
324
+ 5. Structured log entry enables querying and alerting
325
+
326
+ ---
327
+
328
+ ## Centralized Error Handling
329
+
330
+ ### Context
331
+
332
+ Avoid scattered try/catch blocks. Handle errors at defined boundaries.
333
+
334
+ ### Pattern: Error Boundary Middleware
335
+
336
+ ```typescript
337
+ // Express.js example
338
+ function errorHandler(
339
+ error: unknown,
340
+ req: Request,
341
+ res: Response,
342
+ next: NextFunction
343
+ ): void {
344
+ const requestId = req.headers['x-request-id'] as string;
345
+ const { response, logEntry } = handleError(error, requestId, req.user?.id);
346
+
347
+ // Log with appropriate level
348
+ if (error instanceof AppError && error.isOperational) {
349
+ logger.warn(logEntry);
350
+ } else {
351
+ logger.error(logEntry);
352
+ // Alert on programmer errors
353
+ alerting.notify('Unexpected error', logEntry);
354
+ }
355
+
356
+ // Send appropriate status code
357
+ const statusCode = getStatusCode(error);
358
+ res.status(statusCode).json(response);
359
+ }
360
+
361
+ function getStatusCode(error: unknown): number {
362
+ if (error instanceof ValidationError) return 400;
363
+ if (error instanceof NotFoundError) return 404;
364
+ if (error instanceof AuthenticationError) return 401;
365
+ if (error instanceof AuthorizationError) return 403;
366
+ if (error instanceof ExternalServiceError) return 502;
367
+ return 500;
368
+ }
369
+ ```
370
+
371
+ ### Explanation
372
+
373
+ 1. Single error handler for the entire application
374
+ 2. Maps error types to HTTP status codes
375
+ 3. Different log levels for operational vs programmer errors
376
+ 4. Alerts only on unexpected errors to avoid noise
377
+ 5. Controllers throw errors, middleware handles them
378
+
379
+ ---
380
+
381
+ ## Structured Logging
382
+
383
+ ### Context
384
+
385
+ Machine-parseable logs enable querying, aggregation, and alerting.
386
+
387
+ ### Pattern: Structured Log Format
388
+
389
+ ```json
390
+ {
391
+ "timestamp": "2024-01-15T10:23:45.123Z",
392
+ "level": "error",
393
+ "service": "user-service",
394
+ "requestId": "req-abc-123",
395
+ "traceId": "trace-xyz-789",
396
+ "userId": "user-456",
397
+ "operation": "createUser",
398
+ "duration_ms": 234,
399
+ "error": {
400
+ "name": "ValidationError",
401
+ "code": "VALIDATION_ERROR",
402
+ "message": "Email format is invalid",
403
+ "fields": ["email"]
404
+ },
405
+ "metadata": {
406
+ "endpoint": "/api/users",
407
+ "method": "POST"
408
+ }
409
+ }
410
+ ```
411
+
412
+ ### Key Fields
413
+
414
+ | Field | Purpose |
415
+ |-------|---------|
416
+ | `timestamp` | ISO 8601 format for sorting and filtering |
417
+ | `level` | Severity for filtering (error, warn, info, debug) |
418
+ | `service` | Origin service in distributed systems |
419
+ | `requestId` | Links logs for single request |
420
+ | `traceId` | Links logs across services |
421
+ | `operation` | Business operation being performed |
422
+ | `duration_ms` | Performance monitoring |
423
+ | `error` | Structured error details |
424
+
425
+ ### Anti-Pattern
426
+
427
+ ```
428
+ // BAD: Unstructured, hard to parse
429
+ console.log("Error: " + error.message + " for user " + userId);
430
+ ```
431
+
432
+ ---
433
+
434
+ ## Testing Error Paths
435
+
436
+ ### Context
437
+
438
+ Error handling code must be tested as rigorously as success paths.
439
+
440
+ ### Pattern: Test Error Scenarios
441
+
442
+ ```typescript
443
+ describe('UserService', () => {
444
+ describe('createUser', () => {
445
+ it('returns validation errors for invalid email', async () => {
446
+ const result = await userService.createUser({ email: 'invalid', age: 25 });
447
+
448
+ expect(result.isErr()).toBe(true);
449
+ expect(result.error).toBeInstanceOf(ValidationError);
450
+ expect(result.error.context.fields).toContain('email');
451
+ });
452
+
453
+ it('retries on transient database errors', async () => {
454
+ database.save
455
+ .mockRejectedValueOnce(new ConnectionError())
456
+ .mockRejectedValueOnce(new ConnectionError())
457
+ .mockResolvedValueOnce({ id: '123' });
458
+
459
+ const result = await userService.createUser(validInput);
460
+
461
+ expect(result.isOk()).toBe(true);
462
+ expect(database.save).toHaveBeenCalledTimes(3);
463
+ });
464
+
465
+ it('fails fast on constraint violations', async () => {
466
+ database.save.mockRejectedValue(new UniqueConstraintError('email'));
467
+
468
+ const result = await userService.createUser(validInput);
469
+
470
+ expect(result.isErr()).toBe(true);
471
+ expect(database.save).toHaveBeenCalledTimes(1); // No retry
472
+ });
473
+ });
474
+ });
475
+ ```
476
+
477
+ ### What to Test
478
+
479
+ - Validation errors return correct field information
480
+ - Retryable errors trigger retry logic
481
+ - Non-retryable errors fail immediately
482
+ - Error messages are appropriate for audience
483
+ - Sensitive data is not logged
484
+ - Recovery mechanisms work correctly