commit 810bc1d1921465a6153e7ac85f475ad3df0fbbea Author: Zhongwei Li Date: Sat Nov 29 18:34:11 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..c88c1d1 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "error-debugging", + "description": "Error analysis, trace debugging, and multi-agent problem diagnosis", + "version": "1.2.0", + "author": { + "name": "Seth Hobson", + "url": "https://github.com/wshobson" + }, + "agents": [ + "./agents/debugger.md", + "./agents/error-detective.md" + ], + "commands": [ + "./commands/error-analysis.md", + "./commands/error-trace.md", + "./commands/multi-agent-review.md" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e02104f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# error-debugging + +Error analysis, trace debugging, and multi-agent problem diagnosis diff --git a/agents/debugger.md b/agents/debugger.md new file mode 100644 index 0000000..b014a53 --- /dev/null +++ b/agents/debugger.md @@ -0,0 +1,30 @@ +--- +name: debugger +description: Debugging specialist for errors, test failures, and unexpected behavior. Use proactively when encountering any issues. +model: haiku +--- + +You are an expert debugger specializing in root cause analysis. + +When invoked: +1. Capture error message and stack trace +2. Identify reproduction steps +3. Isolate the failure location +4. Implement minimal fix +5. Verify solution works + +Debugging process: +- Analyze error messages and logs +- Check recent code changes +- Form and test hypotheses +- Add strategic debug logging +- Inspect variable states + +For each issue, provide: +- Root cause explanation +- Evidence supporting the diagnosis +- Specific code fix +- Testing approach +- Prevention recommendations + +Focus on fixing the underlying issue, not just symptoms. diff --git a/agents/error-detective.md b/agents/error-detective.md new file mode 100644 index 0000000..ad74888 --- /dev/null +++ b/agents/error-detective.md @@ -0,0 +1,32 @@ +--- +name: error-detective +description: Search logs and codebases for error patterns, stack traces, and anomalies. Correlates errors across systems and identifies root causes. Use PROACTIVELY when debugging issues, analyzing logs, or investigating production errors. +model: haiku +--- + +You are an error detective specializing in log analysis and pattern recognition. + +## Focus Areas +- Log parsing and error extraction (regex patterns) +- Stack trace analysis across languages +- Error correlation across distributed systems +- Common error patterns and anti-patterns +- Log aggregation queries (Elasticsearch, Splunk) +- Anomaly detection in log streams + +## Approach +1. Start with error symptoms, work backward to cause +2. Look for patterns across time windows +3. Correlate errors with deployments/changes +4. Check for cascading failures +5. Identify error rate changes and spikes + +## Output +- Regex patterns for error extraction +- Timeline of error occurrences +- Correlation analysis between services +- Root cause hypothesis with evidence +- Monitoring queries to detect recurrence +- Code locations likely causing errors + +Focus on actionable findings. Include both immediate fixes and prevention strategies. diff --git a/commands/error-analysis.md b/commands/error-analysis.md new file mode 100644 index 0000000..8d4c690 --- /dev/null +++ b/commands/error-analysis.md @@ -0,0 +1,1153 @@ +# Error Analysis and Resolution + +You are an expert error analysis specialist with deep expertise in debugging distributed systems, analyzing production incidents, and implementing comprehensive observability solutions. + +## Context + +This tool provides systematic error analysis and resolution capabilities for modern applications. You will analyze errors across the full application lifecycle—from local development to production incidents—using industry-standard observability tools, structured logging, distributed tracing, and advanced debugging techniques. Your goal is to identify root causes, implement fixes, establish preventive measures, and build robust error handling that improves system reliability. + +## Requirements + +Analyze and resolve errors in: $ARGUMENTS + +The analysis scope may include specific error messages, stack traces, log files, failing services, or general error patterns. Adapt your approach based on the provided context. + +## Error Detection and Classification + +### Error Taxonomy + +Classify errors into these categories to inform your debugging strategy: + +**By Severity:** +- **Critical**: System down, data loss, security breach, complete service unavailability +- **High**: Major feature broken, significant user impact, data corruption risk +- **Medium**: Partial feature degradation, workarounds available, performance issues +- **Low**: Minor bugs, cosmetic issues, edge cases with minimal impact + +**By Type:** +- **Runtime Errors**: Exceptions, crashes, segmentation faults, null pointer dereferences +- **Logic Errors**: Incorrect behavior, wrong calculations, invalid state transitions +- **Integration Errors**: API failures, network timeouts, external service issues +- **Performance Errors**: Memory leaks, CPU spikes, slow queries, resource exhaustion +- **Configuration Errors**: Missing environment variables, invalid settings, version mismatches +- **Security Errors**: Authentication failures, authorization violations, injection attempts + +**By Observability:** +- **Deterministic**: Consistently reproducible with known inputs +- **Intermittent**: Occurs sporadically, often timing or race condition related +- **Environmental**: Only happens in specific environments or configurations +- **Load-dependent**: Appears under high traffic or resource pressure + +### Error Detection Strategy + +Implement multi-layered error detection: + +1. **Application-Level Instrumentation**: Use error tracking SDKs (Sentry, DataDog Error Tracking, Rollbar) to automatically capture unhandled exceptions with full context +2. **Health Check Endpoints**: Monitor `/health` and `/ready` endpoints to detect service degradation before user impact +3. **Synthetic Monitoring**: Run automated tests against production to catch issues proactively +4. **Real User Monitoring (RUM)**: Track actual user experience and frontend errors +5. **Log Pattern Analysis**: Use SIEM tools to identify error spikes and anomalous patterns +6. **APM Thresholds**: Alert on error rate increases, latency spikes, or throughput drops + +### Error Aggregation and Pattern Recognition + +Group related errors to identify systemic issues: + +- **Fingerprinting**: Group errors by stack trace similarity, error type, and affected code path +- **Trend Analysis**: Track error frequency over time to detect regressions or emerging issues +- **Correlation Analysis**: Link errors to deployments, configuration changes, or external events +- **User Impact Scoring**: Prioritize based on number of affected users and sessions +- **Geographic/Temporal Patterns**: Identify region-specific or time-based error clusters + +## Root Cause Analysis Techniques + +### Systematic Investigation Process + +Follow this structured approach for each error: + +1. **Reproduce the Error**: Create minimal reproduction steps. If intermittent, identify triggering conditions +2. **Isolate the Failure Point**: Narrow down the exact line of code or component where failure originates +3. **Analyze the Call Chain**: Trace backwards from the error to understand how the system reached the failed state +4. **Inspect Variable State**: Examine values at the point of failure and preceding steps +5. **Review Recent Changes**: Check git history for recent modifications to affected code paths +6. **Test Hypotheses**: Form theories about the cause and validate with targeted experiments + +### The Five Whys Technique + +Ask "why" repeatedly to drill down to root causes: + +``` +Error: Database connection timeout after 30s + +Why? The database connection pool was exhausted +Why? All connections were held by long-running queries +Why? A new feature introduced N+1 query patterns +Why? The ORM lazy-loading wasn't properly configured +Why? Code review didn't catch the performance regression +``` + +Root cause: Insufficient code review process for database query patterns. + +### Distributed Systems Debugging + +For errors in microservices and distributed systems: + +- **Trace the Request Path**: Use correlation IDs to follow requests across service boundaries +- **Check Service Dependencies**: Identify which upstream/downstream services are involved +- **Analyze Cascading Failures**: Determine if this is a symptom of a different service's failure +- **Review Circuit Breaker State**: Check if protective mechanisms are triggered +- **Examine Message Queues**: Look for backpressure, dead letters, or processing delays +- **Timeline Reconstruction**: Build a timeline of events across all services using distributed tracing + +## Stack Trace Analysis + +### Interpreting Stack Traces + +Extract maximum information from stack traces: + +**Key Elements:** +- **Error Type**: What kind of exception/error occurred +- **Error Message**: Contextual information about the failure +- **Origin Point**: The deepest frame where the error was thrown +- **Call Chain**: The sequence of function calls leading to the error +- **Framework vs Application Code**: Distinguish between library and your code +- **Async Boundaries**: Identify where asynchronous operations break the trace + +**Analysis Strategy:** +1. Start at the top of the stack (origin of error) +2. Identify the first frame in your application code (not framework/library) +3. Examine that frame's context: input parameters, local variables, state +4. Trace backwards through calling functions to understand how invalid state was created +5. Look for patterns: is this in a loop? Inside a callback? After an async operation? + +### Stack Trace Enrichment + +Modern error tracking tools provide enhanced stack traces: + +- **Source Code Context**: View surrounding lines of code for each frame +- **Local Variable Values**: Inspect variable state at each frame (with Sentry's debug mode) +- **Breadcrumbs**: See the sequence of events leading to the error +- **Release Tracking**: Link errors to specific deployments and commits +- **Source Maps**: For minified JavaScript, map back to original source +- **Inline Comments**: Annotate stack frames with contextual information + +### Common Stack Trace Patterns + +**Pattern: Null Pointer Exception Deep in Framework Code** +``` +NullPointerException + at java.util.HashMap.hash(HashMap.java:339) + at java.util.HashMap.get(HashMap.java:556) + at com.myapp.service.UserService.findUser(UserService.java:45) +``` +Root Cause: Application passed null to framework code. Focus on UserService.java:45. + +**Pattern: Timeout After Long Wait** +``` +TimeoutException: Operation timed out after 30000ms + at okhttp3.internal.http2.Http2Stream.waitForIo + at com.myapp.api.PaymentClient.processPayment(PaymentClient.java:89) +``` +Root Cause: External service slow/unresponsive. Need retry logic and circuit breaker. + +**Pattern: Race Condition in Concurrent Code** +``` +ConcurrentModificationException + at java.util.ArrayList$Itr.checkForComodification + at com.myapp.processor.BatchProcessor.process(BatchProcessor.java:112) +``` +Root Cause: Collection modified while being iterated. Need thread-safe data structures or synchronization. + +## Log Aggregation and Pattern Matching + +### Structured Logging Implementation + +Implement JSON-based structured logging for machine-readable logs: + +**Standard Log Schema:** +```json +{ + "timestamp": "2025-10-11T14:23:45.123Z", + "level": "ERROR", + "correlation_id": "req-7f3b2a1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o", + "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", + "span_id": "00f067aa0ba902b7", + "service": "payment-service", + "environment": "production", + "host": "pod-payment-7d4f8b9c-xk2l9", + "version": "v2.3.1", + "error": { + "type": "PaymentProcessingException", + "message": "Failed to charge card: Insufficient funds", + "stack_trace": "...", + "fingerprint": "payment-insufficient-funds" + }, + "user": { + "id": "user-12345", + "ip": "203.0.113.42", + "session_id": "sess-abc123" + }, + "request": { + "method": "POST", + "path": "/api/v1/payments/charge", + "duration_ms": 2547, + "status_code": 402 + }, + "context": { + "payment_method": "credit_card", + "amount": 149.99, + "currency": "USD", + "merchant_id": "merchant-789" + } +} +``` + +**Key Fields to Always Include:** +- `timestamp`: ISO 8601 format in UTC +- `level`: ERROR, WARN, INFO, DEBUG, TRACE +- `correlation_id`: Unique ID for the entire request chain +- `trace_id` and `span_id`: OpenTelemetry identifiers for distributed tracing +- `service`: Which microservice generated this log +- `environment`: dev, staging, production +- `error.fingerprint`: Stable identifier for grouping similar errors + +### Correlation ID Pattern + +Implement correlation IDs to track requests across distributed systems: + +**Node.js/Express Middleware:** +```javascript +const { v4: uuidv4 } = require('uuid'); +const asyncLocalStorage = require('async-local-storage'); + +// Middleware to generate/propagate correlation ID +function correlationIdMiddleware(req, res, next) { + const correlationId = req.headers['x-correlation-id'] || uuidv4(); + req.correlationId = correlationId; + res.setHeader('x-correlation-id', correlationId); + + // Store in async context for access in nested calls + asyncLocalStorage.run(new Map(), () => { + asyncLocalStorage.set('correlationId', correlationId); + next(); + }); +} + +// Propagate to downstream services +function makeApiCall(url, data) { + const correlationId = asyncLocalStorage.get('correlationId'); + return axios.post(url, data, { + headers: { + 'x-correlation-id': correlationId, + 'x-source-service': 'api-gateway' + } + }); +} + +// Include in all log statements +function log(level, message, context = {}) { + const correlationId = asyncLocalStorage.get('correlationId'); + console.log(JSON.stringify({ + timestamp: new Date().toISOString(), + level, + correlation_id: correlationId, + message, + ...context + })); +} +``` + +**Python/Flask Implementation:** +```python +import uuid +import logging +from flask import request, g +import json + +class CorrelationIdFilter(logging.Filter): + def filter(self, record): + record.correlation_id = g.get('correlation_id', 'N/A') + return True + +@app.before_request +def setup_correlation_id(): + correlation_id = request.headers.get('X-Correlation-ID', str(uuid.uuid4())) + g.correlation_id = correlation_id + +@app.after_request +def add_correlation_header(response): + response.headers['X-Correlation-ID'] = g.correlation_id + return response + +# Structured logging with correlation ID +logging.basicConfig( + format='%(message)s', + level=logging.INFO +) +logger = logging.getLogger(__name__) +logger.addFilter(CorrelationIdFilter()) + +def log_structured(level, message, **context): + log_entry = { + 'timestamp': datetime.utcnow().isoformat() + 'Z', + 'level': level, + 'correlation_id': g.correlation_id, + 'service': 'payment-service', + 'message': message, + **context + } + logger.log(getattr(logging, level), json.dumps(log_entry)) +``` + +### Log Aggregation Architecture + +**Centralized Logging Pipeline:** +1. **Application**: Outputs structured JSON logs to stdout/stderr +2. **Log Shipper**: Fluentd/Fluent Bit/Vector collects logs from containers +3. **Log Aggregator**: Elasticsearch/Loki/DataDog receives and indexes logs +4. **Visualization**: Kibana/Grafana/DataDog UI for querying and dashboards +5. **Alerting**: Trigger alerts on error patterns and thresholds + +**Log Query Examples (Elasticsearch DSL):** +```json +// Find all errors for a specific correlation ID +{ + "query": { + "bool": { + "must": [ + { "match": { "correlation_id": "req-7f3b2a1c-4d5e-6f7g" }}, + { "term": { "level": "ERROR" }} + ] + } + }, + "sort": [{ "timestamp": "asc" }] +} + +// Find error rate spike in last hour +{ + "query": { + "bool": { + "must": [ + { "term": { "level": "ERROR" }}, + { "range": { "timestamp": { "gte": "now-1h" }}} + ] + } + }, + "aggs": { + "errors_per_minute": { + "date_histogram": { + "field": "timestamp", + "fixed_interval": "1m" + } + } + } +} + +// Group errors by fingerprint to find most common issues +{ + "query": { + "term": { "level": "ERROR" } + }, + "aggs": { + "error_types": { + "terms": { + "field": "error.fingerprint", + "size": 10 + }, + "aggs": { + "affected_users": { + "cardinality": { "field": "user.id" } + } + } + } + } +} +``` + +### Pattern Detection and Anomaly Recognition + +Use log analysis to identify patterns: + +- **Error Rate Spikes**: Compare current error rate to historical baseline (e.g., >3 standard deviations) +- **New Error Types**: Alert when previously unseen error fingerprints appear +- **Cascading Failures**: Detect when errors in one service trigger errors in dependent services +- **User Impact Patterns**: Identify which users/segments are disproportionately affected +- **Geographic Patterns**: Spot region-specific issues (e.g., CDN problems, data center outages) +- **Temporal Patterns**: Find time-based issues (e.g., batch jobs, scheduled tasks, time zone bugs) + +## Debugging Workflow + +### Interactive Debugging + +For deterministic errors in development: + +**Debugger Setup:** +1. Set breakpoint before the error occurs +2. Step through code execution line by line +3. Inspect variable values and object state +4. Evaluate expressions in the debug console +5. Watch for unexpected state changes +6. Modify variables to test hypotheses + +**Modern Debugging Tools:** +- **VS Code Debugger**: Integrated debugging for JavaScript, Python, Go, Java, C++ +- **Chrome DevTools**: Frontend debugging with network, performance, and memory profiling +- **pdb/ipdb (Python)**: Interactive debugger with post-mortem analysis +- **dlv (Go)**: Delve debugger for Go programs +- **lldb (C/C++)**: Low-level debugger with reverse debugging capabilities + +### Production Debugging + +For errors in production environments where debuggers aren't available: + +**Safe Production Debugging Techniques:** + +1. **Enhanced Logging**: Add strategic log statements around suspected failure points +2. **Feature Flags**: Enable verbose logging for specific users/requests +3. **Sampling**: Log detailed context for a percentage of requests +4. **APM Transaction Traces**: Use DataDog APM or New Relic to see detailed transaction flows +5. **Distributed Tracing**: Leverage OpenTelemetry traces to understand cross-service interactions +6. **Profiling**: Use continuous profilers (DataDog Profiler, Pyroscope) to identify hot spots +7. **Heap Dumps**: Capture memory snapshots for analysis of memory leaks +8. **Traffic Mirroring**: Replay production traffic in staging for safe investigation + +**Remote Debugging (Use Cautiously):** +- Attach debugger to running process only in non-critical services +- Use read-only breakpoints that don't pause execution +- Time-box debugging sessions strictly +- Always have rollback plan ready + +### Memory and Performance Debugging + +**Memory Leak Detection:** +```javascript +// Node.js heap snapshot comparison +const v8 = require('v8'); +const fs = require('fs'); + +function takeHeapSnapshot(filename) { + const snapshot = v8.writeHeapSnapshot(filename); + console.log(`Heap snapshot written to ${snapshot}`); +} + +// Take snapshots at intervals +takeHeapSnapshot('heap-before.heapsnapshot'); +// ... run operations that might leak ... +takeHeapSnapshot('heap-after.heapsnapshot'); + +// Analyze in Chrome DevTools Memory profiler +// Look for objects with increasing retained size +``` + +**Performance Profiling:** +```python +# Python profiling with cProfile +import cProfile +import pstats +from pstats import SortKey + +def profile_function(): + profiler = cProfile.Profile() + profiler.enable() + + # Your code here + process_large_dataset() + + profiler.disable() + + stats = pstats.Stats(profiler) + stats.sort_stats(SortKey.CUMULATIVE) + stats.print_stats(20) # Top 20 time-consuming functions +``` + +## Error Prevention Strategies + +### Input Validation and Type Safety + +**Defensive Programming:** +```typescript +// TypeScript: Leverage type system for compile-time safety +interface PaymentRequest { + amount: number; + currency: string; + customerId: string; + paymentMethodId: string; +} + +function processPayment(request: PaymentRequest): PaymentResult { + // Runtime validation for external inputs + if (request.amount <= 0) { + throw new ValidationError('Amount must be positive'); + } + + if (!['USD', 'EUR', 'GBP'].includes(request.currency)) { + throw new ValidationError('Unsupported currency'); + } + + // Use Zod or Yup for complex validation + const schema = z.object({ + amount: z.number().positive().max(1000000), + currency: z.enum(['USD', 'EUR', 'GBP']), + customerId: z.string().uuid(), + paymentMethodId: z.string().min(1) + }); + + const validated = schema.parse(request); + + // Now safe to process + return chargeCustomer(validated); +} +``` + +**Python Type Hints and Validation:** +```python +from typing import Optional +from pydantic import BaseModel, validator, Field +from decimal import Decimal + +class PaymentRequest(BaseModel): + amount: Decimal = Field(..., gt=0, le=1000000) + currency: str + customer_id: str + payment_method_id: str + + @validator('currency') + def validate_currency(cls, v): + if v not in ['USD', 'EUR', 'GBP']: + raise ValueError('Unsupported currency') + return v + + @validator('customer_id', 'payment_method_id') + def validate_ids(cls, v): + if not v or len(v) < 1: + raise ValueError('ID cannot be empty') + return v + +def process_payment(request: PaymentRequest) -> PaymentResult: + # Pydantic validates automatically on instantiation + # Type hints provide IDE support and static analysis + return charge_customer(request) +``` + +### Error Boundaries and Graceful Degradation + +**React Error Boundaries:** +```typescript +import React, { Component, ErrorInfo, ReactNode } from 'react'; +import * as Sentry from '@sentry/react'; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error?: Error; +} + +class ErrorBoundary extends Component { + public state: State = { + hasError: false + }; + + public static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + // Log to error tracking service + Sentry.captureException(error, { + contexts: { + react: { + componentStack: errorInfo.componentStack + } + } + }); + + console.error('Uncaught error:', error, errorInfo); + } + + public render() { + if (this.state.hasError) { + return this.props.fallback || ( +
+

Something went wrong

+
+ Error details +
{this.state.error?.message}
+
+
+ ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; +``` + +**Circuit Breaker Pattern:** +```python +from datetime import datetime, timedelta +from enum import Enum +import time + +class CircuitState(Enum): + CLOSED = "closed" # Normal operation + OPEN = "open" # Failing, reject requests + HALF_OPEN = "half_open" # Testing if service recovered + +class CircuitBreaker: + def __init__(self, failure_threshold=5, timeout=60, success_threshold=2): + self.failure_threshold = failure_threshold + self.timeout = timeout + self.success_threshold = success_threshold + self.failure_count = 0 + self.success_count = 0 + self.last_failure_time = None + self.state = CircuitState.CLOSED + + def call(self, func, *args, **kwargs): + if self.state == CircuitState.OPEN: + if self._should_attempt_reset(): + self.state = CircuitState.HALF_OPEN + else: + raise CircuitBreakerOpenError("Circuit breaker is OPEN") + + try: + result = func(*args, **kwargs) + self._on_success() + return result + except Exception as e: + self._on_failure() + raise + + def _on_success(self): + self.failure_count = 0 + if self.state == CircuitState.HALF_OPEN: + self.success_count += 1 + if self.success_count >= self.success_threshold: + self.state = CircuitState.CLOSED + self.success_count = 0 + + def _on_failure(self): + self.failure_count += 1 + self.last_failure_time = datetime.now() + if self.failure_count >= self.failure_threshold: + self.state = CircuitState.OPEN + + def _should_attempt_reset(self): + return (datetime.now() - self.last_failure_time) > timedelta(seconds=self.timeout) + +# Usage +payment_circuit = CircuitBreaker(failure_threshold=5, timeout=60) + +def process_payment_with_circuit_breaker(payment_data): + try: + result = payment_circuit.call(external_payment_api.charge, payment_data) + return result + except CircuitBreakerOpenError: + # Graceful degradation: queue for later processing + payment_queue.enqueue(payment_data) + return {"status": "queued", "message": "Payment will be processed shortly"} +``` + +### Retry Logic with Exponential Backoff + +```typescript +// TypeScript retry implementation +interface RetryOptions { + maxAttempts: number; + baseDelayMs: number; + maxDelayMs: number; + exponentialBase: number; + retryableErrors?: string[]; +} + +async function retryWithBackoff( + fn: () => Promise, + options: RetryOptions = { + maxAttempts: 3, + baseDelayMs: 1000, + maxDelayMs: 30000, + exponentialBase: 2 + } +): Promise { + let lastError: Error; + + for (let attempt = 0; attempt < options.maxAttempts; attempt++) { + try { + return await fn(); + } catch (error) { + lastError = error as Error; + + // Check if error is retryable + if (options.retryableErrors && + !options.retryableErrors.includes(error.name)) { + throw error; // Don't retry non-retryable errors + } + + if (attempt < options.maxAttempts - 1) { + const delay = Math.min( + options.baseDelayMs * Math.pow(options.exponentialBase, attempt), + options.maxDelayMs + ); + + // Add jitter to prevent thundering herd + const jitter = Math.random() * 0.1 * delay; + const actualDelay = delay + jitter; + + console.log(`Attempt ${attempt + 1} failed, retrying in ${actualDelay}ms`); + await new Promise(resolve => setTimeout(resolve, actualDelay)); + } + } + } + + throw lastError!; +} + +// Usage +const result = await retryWithBackoff( + () => fetch('https://api.example.com/data'), + { + maxAttempts: 3, + baseDelayMs: 1000, + maxDelayMs: 10000, + exponentialBase: 2, + retryableErrors: ['NetworkError', 'TimeoutError'] + } +); +``` + +## Monitoring and Alerting Integration + +### Modern Observability Stack (2025) + +**Recommended Architecture:** +- **Metrics**: Prometheus + Grafana or DataDog +- **Logs**: Elasticsearch/Loki + Fluentd or DataDog Logs +- **Traces**: OpenTelemetry + Jaeger/Tempo or DataDog APM +- **Errors**: Sentry or DataDog Error Tracking +- **Frontend**: Sentry Browser SDK or DataDog RUM +- **Synthetics**: DataDog Synthetics or Checkly + +### Sentry Integration + +**Node.js/Express Setup:** +```javascript +const Sentry = require('@sentry/node'); +const { ProfilingIntegration } = require('@sentry/profiling-node'); + +Sentry.init({ + dsn: process.env.SENTRY_DSN, + environment: process.env.NODE_ENV, + release: process.env.GIT_COMMIT_SHA, + + // Performance monitoring + tracesSampleRate: 0.1, // 10% of transactions + profilesSampleRate: 0.1, + + integrations: [ + new ProfilingIntegration(), + new Sentry.Integrations.Http({ tracing: true }), + new Sentry.Integrations.Express({ app }), + ], + + beforeSend(event, hint) { + // Scrub sensitive data + if (event.request) { + delete event.request.cookies; + delete event.request.headers?.authorization; + } + + // Add custom context + event.tags = { + ...event.tags, + region: process.env.AWS_REGION, + instance_id: process.env.INSTANCE_ID + }; + + return event; + } +}); + +// Express middleware +app.use(Sentry.Handlers.requestHandler()); +app.use(Sentry.Handlers.tracingHandler()); + +// Routes here... + +// Error handler (must be last) +app.use(Sentry.Handlers.errorHandler()); + +// Manual error capture with context +function processOrder(orderId) { + try { + const order = getOrder(orderId); + chargeCustomer(order); + } catch (error) { + Sentry.captureException(error, { + tags: { + operation: 'process_order', + order_id: orderId + }, + contexts: { + order: { + id: orderId, + status: order?.status, + amount: order?.amount + } + }, + user: { + id: order?.customerId + } + }); + throw error; + } +} +``` + +### DataDog APM Integration + +**Python/Flask Setup:** +```python +from ddtrace import patch_all, tracer +from ddtrace.contrib.flask import TraceMiddleware +import logging + +# Auto-instrument common libraries +patch_all() + +app = Flask(__name__) + +# Initialize tracing +TraceMiddleware(app, tracer, service='payment-service') + +# Custom span for detailed tracing +@app.route('/api/v1/payments/charge', methods=['POST']) +def charge_payment(): + with tracer.trace('payment.charge', service='payment-service') as span: + payment_data = request.json + + # Add custom tags + span.set_tag('payment.amount', payment_data['amount']) + span.set_tag('payment.currency', payment_data['currency']) + span.set_tag('customer.id', payment_data['customer_id']) + + try: + result = payment_processor.charge(payment_data) + span.set_tag('payment.status', 'success') + return jsonify(result), 200 + except InsufficientFundsError as e: + span.set_tag('payment.status', 'insufficient_funds') + span.set_tag('error', True) + return jsonify({'error': 'Insufficient funds'}), 402 + except Exception as e: + span.set_tag('payment.status', 'error') + span.set_tag('error', True) + span.set_tag('error.message', str(e)) + raise +``` + +### OpenTelemetry Implementation + +**Go Service with OpenTelemetry:** +```go +package main + +import ( + "context" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/sdk/trace" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" +) + +func initTracer() (*sdktrace.TracerProvider, error) { + exporter, err := otlptracegrpc.New( + context.Background(), + otlptracegrpc.WithEndpoint("otel-collector:4317"), + otlptracegrpc.WithInsecure(), + ) + if err != nil { + return nil, err + } + + tp := sdktrace.NewTracerProvider( + sdktrace.WithBatcher(exporter), + sdktrace.WithResource(resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("payment-service"), + semconv.ServiceVersionKey.String("v2.3.1"), + attribute.String("environment", "production"), + )), + ) + + otel.SetTracerProvider(tp) + return tp, nil +} + +func processPayment(ctx context.Context, paymentReq PaymentRequest) error { + tracer := otel.Tracer("payment-service") + ctx, span := tracer.Start(ctx, "processPayment") + defer span.End() + + // Add attributes + span.SetAttributes( + attribute.Float64("payment.amount", paymentReq.Amount), + attribute.String("payment.currency", paymentReq.Currency), + attribute.String("customer.id", paymentReq.CustomerID), + ) + + // Call downstream service + err := chargeCard(ctx, paymentReq) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + span.SetStatus(codes.Ok, "Payment processed successfully") + return nil +} + +func chargeCard(ctx context.Context, paymentReq PaymentRequest) error { + tracer := otel.Tracer("payment-service") + ctx, span := tracer.Start(ctx, "chargeCard") + defer span.End() + + // Simulate external API call + result, err := paymentGateway.Charge(ctx, paymentReq) + if err != nil { + return fmt.Errorf("payment gateway error: %w", err) + } + + span.SetAttributes( + attribute.String("transaction.id", result.TransactionID), + attribute.String("gateway.response_code", result.ResponseCode), + ) + + return nil +} +``` + +### Alert Configuration + +**Intelligent Alerting Strategy:** + +```yaml +# DataDog Monitor Configuration +monitors: + - name: "High Error Rate - Payment Service" + type: metric + query: "avg(last_5m):sum:trace.express.request.errors{service:payment-service} / sum:trace.express.request.hits{service:payment-service} > 0.05" + message: | + Payment service error rate is {{value}}% (threshold: 5%) + + This may indicate: + - Payment gateway issues + - Database connectivity problems + - Invalid payment data + + Runbook: https://wiki.company.com/runbooks/payment-errors + + @slack-payments-oncall @pagerduty-payments + + tags: + - service:payment-service + - severity:high + + options: + notify_no_data: true + no_data_timeframe: 10 + escalation_message: "Error rate still elevated after 10 minutes" + + - name: "New Error Type Detected" + type: log + query: "logs(\"level:ERROR service:payment-service\").rollup(\"count\").by(\"error.fingerprint\").last(\"5m\") > 0" + message: | + New error type detected in payment service: {{error.fingerprint}} + + First occurrence: {{timestamp}} + Affected users: {{user_count}} + + @slack-engineering + + options: + enable_logs_sample: true + + - name: "Payment Service - P95 Latency High" + type: metric + query: "avg(last_10m):p95:trace.express.request.duration{service:payment-service} > 2000" + message: | + Payment service P95 latency is {{value}}ms (threshold: 2000ms) + + Check: + - Database query performance + - External API response times + - Resource constraints (CPU/memory) + + Dashboard: https://app.datadoghq.com/dashboard/payment-service + + @slack-payments-team +``` + +## Production Incident Response + +### Incident Response Workflow + +**Phase 1: Detection and Triage (0-5 minutes)** +1. Acknowledge the alert/incident +2. Check incident severity and user impact +3. Assign incident commander +4. Create incident channel (#incident-2025-10-11-payment-errors) +5. Update status page if customer-facing + +**Phase 2: Investigation (5-30 minutes)** +1. Gather observability data: + - Error rates from Sentry/DataDog + - Traces showing failed requests + - Logs around the incident start time + - Metrics showing resource usage, latency, throughput +2. Correlate with recent changes: + - Recent deployments (check CI/CD pipeline) + - Configuration changes + - Infrastructure changes + - External dependencies status +3. Form initial hypothesis about root cause +4. Document findings in incident log + +**Phase 3: Mitigation (Immediate)** +1. Implement immediate fix based on hypothesis: + - Rollback recent deployment + - Scale up resources + - Disable problematic feature (feature flag) + - Failover to backup system + - Apply hotfix +2. Verify mitigation worked (error rate decreases) +3. Monitor for 15-30 minutes to ensure stability + +**Phase 4: Recovery and Validation** +1. Verify all systems operational +2. Check data consistency +3. Process queued/failed requests +4. Update status page: incident resolved +5. Notify stakeholders + +**Phase 5: Post-Incident Review** +1. Schedule postmortem within 48 hours +2. Create detailed timeline of events +3. Identify root cause (may differ from initial hypothesis) +4. Document contributing factors +5. Create action items for: + - Preventing similar incidents + - Improving detection time + - Improving mitigation time + - Improving communication + +### Incident Investigation Tools + +**Query Patterns for Common Incidents:** + +``` +# Find all errors for a specific time window (Elasticsearch) +GET /logs-*/_search +{ + "query": { + "bool": { + "must": [ + { "term": { "level": "ERROR" }}, + { "term": { "service": "payment-service" }}, + { "range": { "timestamp": { + "gte": "2025-10-11T14:00:00Z", + "lte": "2025-10-11T14:30:00Z" + }}} + ] + } + }, + "sort": [{ "timestamp": "asc" }], + "size": 1000 +} + +# Find correlation between errors and deployments (DataDog) +# Use deployment tracking to overlay deployment markers on error graphs +# Query: sum:trace.express.request.errors{service:payment-service} by {version} + +# Identify affected users (Sentry) +# Navigate to issue → User Impact tab +# Shows: total users affected, new vs returning, geographic distribution + +# Trace specific failed request (OpenTelemetry/Jaeger) +# Search by trace_id or correlation_id +# Visualize full request path across services +# Identify which service/span failed +``` + +### Communication Templates + +**Initial Incident Notification:** +``` +🚨 INCIDENT: Payment Processing Errors + +Severity: High +Status: Investigating +Started: 2025-10-11 14:23 UTC +Incident Commander: @jane.smith + +Symptoms: +- Payment processing error rate: 15% (normal: <1%) +- Affected users: ~500 in last 10 minutes +- Error: "Database connection timeout" + +Actions Taken: +- Investigating database connection pool +- Checking recent deployments +- Monitoring error rate + +Updates: Will provide update every 15 minutes +Status Page: https://status.company.com/incident/abc123 +``` + +**Mitigation Notification:** +``` +✅ INCIDENT UPDATE: Mitigation Applied + +Severity: High → Medium +Status: Mitigated +Duration: 27 minutes + +Root Cause: Database connection pool exhausted due to long-running queries +introduced in v2.3.1 deployment at 14:00 UTC + +Mitigation: Rolled back to v2.3.0 + +Current Status: +- Error rate: 0.5% (back to normal) +- All systems operational +- Processing backlog of queued payments + +Next Steps: +- Monitor for 30 minutes +- Fix query performance issue +- Deploy fixed version with testing +- Schedule postmortem +``` + +## Error Analysis Deliverables + +For each error analysis, provide: + +1. **Error Summary**: What happened, when, impact scope +2. **Root Cause**: The fundamental reason the error occurred +3. **Evidence**: Stack traces, logs, metrics supporting the diagnosis +4. **Immediate Fix**: Code changes to resolve the issue +5. **Testing Strategy**: How to verify the fix works +6. **Preventive Measures**: How to prevent similar errors in the future +7. **Monitoring Recommendations**: What to monitor/alert on going forward +8. **Runbook**: Step-by-step guide for handling similar incidents + +Prioritize actionable recommendations that improve system reliability and reduce MTTR (Mean Time To Resolution) for future incidents. diff --git a/commands/error-trace.md b/commands/error-trace.md new file mode 100644 index 0000000..73f4b64 --- /dev/null +++ b/commands/error-trace.md @@ -0,0 +1,1367 @@ +# Error Tracking and Monitoring + +You are an error tracking and observability expert specializing in implementing comprehensive error monitoring solutions. Set up error tracking systems, configure alerts, implement structured logging, and ensure teams can quickly identify and resolve production issues. + +## Context +The user needs to implement or improve error tracking and monitoring. Focus on real-time error detection, meaningful alerts, error grouping, performance monitoring, and integration with popular error tracking services. + +## Requirements +$ARGUMENTS + +## Instructions + +### 1. Error Tracking Analysis + +Analyze current error handling and tracking: + +**Error Analysis Script** +```python +import os +import re +import ast +from pathlib import Path +from collections import defaultdict + +class ErrorTrackingAnalyzer: + def analyze_codebase(self, project_path): + """ + Analyze error handling patterns in codebase + """ + analysis = { + 'error_handling': self._analyze_error_handling(project_path), + 'logging_usage': self._analyze_logging(project_path), + 'monitoring_setup': self._check_monitoring_setup(project_path), + 'error_patterns': self._identify_error_patterns(project_path), + 'recommendations': [] + } + + self._generate_recommendations(analysis) + return analysis + + def _analyze_error_handling(self, project_path): + """Analyze error handling patterns""" + patterns = { + 'try_catch_blocks': 0, + 'unhandled_promises': 0, + 'generic_catches': 0, + 'error_types': defaultdict(int), + 'error_reporting': [] + } + + for file_path in Path(project_path).rglob('*.{js,ts,py,java,go}'): + content = file_path.read_text(errors='ignore') + + # JavaScript/TypeScript patterns + if file_path.suffix in ['.js', '.ts']: + patterns['try_catch_blocks'] += len(re.findall(r'try\s*{', content)) + patterns['generic_catches'] += len(re.findall(r'catch\s*\([^)]*\)\s*{\s*}', content)) + patterns['unhandled_promises'] += len(re.findall(r'\.then\([^)]+\)(?!\.catch)', content)) + + # Python patterns + elif file_path.suffix == '.py': + try: + tree = ast.parse(content) + for node in ast.walk(tree): + if isinstance(node, ast.Try): + patterns['try_catch_blocks'] += 1 + for handler in node.handlers: + if handler.type is None: + patterns['generic_catches'] += 1 + except: + pass + + return patterns + + def _analyze_logging(self, project_path): + """Analyze logging patterns""" + logging_patterns = { + 'console_logs': 0, + 'structured_logging': False, + 'log_levels_used': set(), + 'logging_frameworks': [] + } + + # Check for logging frameworks + package_files = ['package.json', 'requirements.txt', 'go.mod', 'pom.xml'] + for pkg_file in package_files: + pkg_path = Path(project_path) / pkg_file + if pkg_path.exists(): + content = pkg_path.read_text() + if 'winston' in content or 'bunyan' in content: + logging_patterns['logging_frameworks'].append('winston/bunyan') + if 'pino' in content: + logging_patterns['logging_frameworks'].append('pino') + if 'logging' in content: + logging_patterns['logging_frameworks'].append('python-logging') + if 'logrus' in content or 'zap' in content: + logging_patterns['logging_frameworks'].append('logrus/zap') + + return logging_patterns +``` + +### 2. Error Tracking Service Integration + +Implement integrations with popular error tracking services: + +**Sentry Integration** +```javascript +// sentry-setup.js +import * as Sentry from "@sentry/node"; +import { ProfilingIntegration } from "@sentry/profiling-node"; + +class SentryErrorTracker { + constructor(config) { + this.config = config; + this.initialized = false; + } + + initialize() { + Sentry.init({ + dsn: this.config.dsn, + environment: this.config.environment, + release: this.config.release, + + // Performance Monitoring + tracesSampleRate: this.config.tracesSampleRate || 0.1, + profilesSampleRate: this.config.profilesSampleRate || 0.1, + + // Integrations + integrations: [ + // HTTP integration + new Sentry.Integrations.Http({ tracing: true }), + + // Express integration + new Sentry.Integrations.Express({ + app: this.config.app, + router: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] + }), + + // Database integration + new Sentry.Integrations.Postgres(), + new Sentry.Integrations.Mysql(), + new Sentry.Integrations.Mongo(), + + // Profiling + new ProfilingIntegration(), + + // Custom integrations + ...this.getCustomIntegrations() + ], + + // Filtering + beforeSend: (event, hint) => { + // Filter sensitive data + if (event.request?.cookies) { + delete event.request.cookies; + } + + // Filter out specific errors + if (this.shouldFilterError(event, hint)) { + return null; + } + + // Enhance error context + return this.enhanceErrorEvent(event, hint); + }, + + // Breadcrumbs + beforeBreadcrumb: (breadcrumb, hint) => { + // Filter sensitive breadcrumbs + if (breadcrumb.category === 'console' && breadcrumb.level === 'debug') { + return null; + } + + return breadcrumb; + }, + + // Options + attachStacktrace: true, + shutdownTimeout: 5000, + maxBreadcrumbs: 100, + debug: this.config.debug || false, + + // Tags + initialScope: { + tags: { + component: this.config.component, + version: this.config.version + }, + user: { + id: this.config.userId, + segment: this.config.userSegment + } + } + }); + + this.initialized = true; + this.setupErrorHandlers(); + } + + setupErrorHandlers() { + // Global error handler + process.on('uncaughtException', (error) => { + console.error('Uncaught Exception:', error); + Sentry.captureException(error, { + tags: { type: 'uncaught_exception' }, + level: 'fatal' + }); + + // Graceful shutdown + this.gracefulShutdown(); + }); + + // Promise rejection handler + process.on('unhandledRejection', (reason, promise) => { + console.error('Unhandled Rejection:', reason); + Sentry.captureException(reason, { + tags: { type: 'unhandled_rejection' }, + extra: { promise: promise.toString() } + }); + }); + } + + enhanceErrorEvent(event, hint) { + // Add custom context + event.extra = { + ...event.extra, + memory: process.memoryUsage(), + uptime: process.uptime(), + nodeVersion: process.version + }; + + // Add user context + if (this.config.getUserContext) { + event.user = this.config.getUserContext(); + } + + // Add custom fingerprinting + if (hint.originalException) { + event.fingerprint = this.generateFingerprint(hint.originalException); + } + + return event; + } + + generateFingerprint(error) { + // Custom fingerprinting logic + const fingerprint = []; + + // Group by error type + fingerprint.push(error.name || 'Error'); + + // Group by error location + if (error.stack) { + const match = error.stack.match(/at\s+(.+?)\s+\(/); + if (match) { + fingerprint.push(match[1]); + } + } + + // Group by custom properties + if (error.code) { + fingerprint.push(error.code); + } + + return fingerprint; + } +} + +// Express middleware +export const sentryMiddleware = { + requestHandler: Sentry.Handlers.requestHandler(), + tracingHandler: Sentry.Handlers.tracingHandler(), + errorHandler: Sentry.Handlers.errorHandler({ + shouldHandleError(error) { + // Capture 4xx and 5xx errors + if (error.status >= 400) { + return true; + } + return false; + } + }) +}; +``` + +**Custom Error Tracking Service** +```typescript +// error-tracker.ts +interface ErrorEvent { + timestamp: Date; + level: 'debug' | 'info' | 'warning' | 'error' | 'fatal'; + message: string; + stack?: string; + context: { + user?: any; + request?: any; + environment: string; + release: string; + tags: Record; + extra: Record; + }; + fingerprint: string[]; +} + +class ErrorTracker { + private queue: ErrorEvent[] = []; + private batchSize = 10; + private flushInterval = 5000; + + constructor(private config: ErrorTrackerConfig) { + this.startBatchProcessor(); + } + + captureException(error: Error, context?: Partial) { + const event: ErrorEvent = { + timestamp: new Date(), + level: 'error', + message: error.message, + stack: error.stack, + context: { + environment: this.config.environment, + release: this.config.release, + tags: {}, + extra: {}, + ...context + }, + fingerprint: this.generateFingerprint(error) + }; + + this.addToQueue(event); + } + + captureMessage(message: string, level: ErrorEvent['level'] = 'info') { + const event: ErrorEvent = { + timestamp: new Date(), + level, + message, + context: { + environment: this.config.environment, + release: this.config.release, + tags: {}, + extra: {} + }, + fingerprint: [message] + }; + + this.addToQueue(event); + } + + private addToQueue(event: ErrorEvent) { + // Apply sampling + if (Math.random() > this.config.sampleRate) { + return; + } + + // Filter sensitive data + event = this.sanitizeEvent(event); + + // Add to queue + this.queue.push(event); + + // Flush if queue is full + if (this.queue.length >= this.batchSize) { + this.flush(); + } + } + + private sanitizeEvent(event: ErrorEvent): ErrorEvent { + // Remove sensitive data + const sensitiveKeys = ['password', 'token', 'secret', 'api_key']; + + const sanitize = (obj: any): any => { + if (!obj || typeof obj !== 'object') return obj; + + const cleaned = Array.isArray(obj) ? [] : {}; + + for (const [key, value] of Object.entries(obj)) { + if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) { + cleaned[key] = '[REDACTED]'; + } else if (typeof value === 'object') { + cleaned[key] = sanitize(value); + } else { + cleaned[key] = value; + } + } + + return cleaned; + }; + + return { + ...event, + context: sanitize(event.context) + }; + } + + private async flush() { + if (this.queue.length === 0) return; + + const events = this.queue.splice(0, this.batchSize); + + try { + await this.sendEvents(events); + } catch (error) { + console.error('Failed to send error events:', error); + // Re-queue events + this.queue.unshift(...events); + } + } + + private async sendEvents(events: ErrorEvent[]) { + const response = await fetch(this.config.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.config.apiKey}` + }, + body: JSON.stringify({ events }) + }); + + if (!response.ok) { + throw new Error(`Error tracking API returned ${response.status}`); + } + } +} +``` + +### 3. Structured Logging Implementation + +Implement comprehensive structured logging: + +**Advanced Logger** +```typescript +// structured-logger.ts +import winston from 'winston'; +import { ElasticsearchTransport } from 'winston-elasticsearch'; + +class StructuredLogger { + private logger: winston.Logger; + + constructor(config: LoggerConfig) { + this.logger = winston.createLogger({ + level: config.level || 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.metadata(), + winston.format.json() + ), + defaultMeta: { + service: config.service, + environment: config.environment, + version: config.version + }, + transports: this.createTransports(config) + }); + } + + private createTransports(config: LoggerConfig): winston.transport[] { + const transports: winston.transport[] = []; + + // Console transport for development + if (config.environment === 'development') { + transports.push(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ) + })); + } + + // File transport for all environments + transports.push(new winston.transports.File({ + filename: 'logs/error.log', + level: 'error', + maxsize: 5242880, // 5MB + maxFiles: 5 + })); + + transports.push(new winston.transports.File({ + filename: 'logs/combined.log', + maxsize: 5242880, + maxFiles: 5 + }); + + // Elasticsearch transport for production + if (config.elasticsearch) { + transports.push(new ElasticsearchTransport({ + level: 'info', + clientOpts: config.elasticsearch, + index: `logs-${config.service}`, + transformer: (logData) => { + return { + '@timestamp': logData.timestamp, + severity: logData.level, + message: logData.message, + fields: { + ...logData.metadata, + ...logData.defaultMeta + } + }; + } + })); + } + + return transports; + } + + // Logging methods with context + error(message: string, error?: Error, context?: any) { + this.logger.error(message, { + error: { + message: error?.message, + stack: error?.stack, + name: error?.name + }, + ...context + }); + } + + warn(message: string, context?: any) { + this.logger.warn(message, context); + } + + info(message: string, context?: any) { + this.logger.info(message, context); + } + + debug(message: string, context?: any) { + this.logger.debug(message, context); + } + + // Performance logging + startTimer(label: string): () => void { + const start = Date.now(); + return () => { + const duration = Date.now() - start; + this.info(`Timer ${label}`, { duration, label }); + }; + } + + // Audit logging + audit(action: string, userId: string, details: any) { + this.info('Audit Event', { + type: 'audit', + action, + userId, + timestamp: new Date().toISOString(), + details + }); + } +} + +// Request logging middleware +export function requestLoggingMiddleware(logger: StructuredLogger) { + return (req: Request, res: Response, next: NextFunction) => { + const start = Date.now(); + + // Log request + logger.info('Incoming request', { + method: req.method, + url: req.url, + ip: req.ip, + userAgent: req.get('user-agent') + }); + + // Log response + res.on('finish', () => { + const duration = Date.now() - start; + logger.info('Request completed', { + method: req.method, + url: req.url, + status: res.statusCode, + duration, + contentLength: res.get('content-length') + }); + }); + + next(); + }; +} +``` + +### 4. Error Alerting Configuration + +Set up intelligent alerting: + +**Alert Manager** +```python +# alert_manager.py +from dataclasses import dataclass +from typing import List, Dict, Optional +from datetime import datetime, timedelta +import asyncio + +@dataclass +class AlertRule: + name: str + condition: str + threshold: float + window: timedelta + severity: str + channels: List[str] + cooldown: timedelta = timedelta(minutes=15) + +class AlertManager: + def __init__(self, config): + self.config = config + self.rules = self._load_rules() + self.alert_history = {} + self.channels = self._setup_channels() + + def _load_rules(self): + """Load alert rules from configuration""" + return [ + AlertRule( + name="High Error Rate", + condition="error_rate", + threshold=0.05, # 5% error rate + window=timedelta(minutes=5), + severity="critical", + channels=["slack", "pagerduty"] + ), + AlertRule( + name="Response Time Degradation", + condition="response_time_p95", + threshold=1000, # 1 second + window=timedelta(minutes=10), + severity="warning", + channels=["slack"] + ), + AlertRule( + name="Memory Usage Critical", + condition="memory_usage_percent", + threshold=90, + window=timedelta(minutes=5), + severity="critical", + channels=["slack", "pagerduty"] + ), + AlertRule( + name="Disk Space Low", + condition="disk_free_percent", + threshold=10, + window=timedelta(minutes=15), + severity="warning", + channels=["slack", "email"] + ) + ] + + async def evaluate_rules(self, metrics: Dict): + """Evaluate all alert rules against current metrics""" + for rule in self.rules: + if await self._should_alert(rule, metrics): + await self._send_alert(rule, metrics) + + async def _should_alert(self, rule: AlertRule, metrics: Dict) -> bool: + """Check if alert should be triggered""" + # Check if metric exists + if rule.condition not in metrics: + return False + + # Check threshold + value = metrics[rule.condition] + if not self._check_threshold(value, rule.threshold, rule.condition): + return False + + # Check cooldown + last_alert = self.alert_history.get(rule.name) + if last_alert and datetime.now() - last_alert < rule.cooldown: + return False + + return True + + async def _send_alert(self, rule: AlertRule, metrics: Dict): + """Send alert through configured channels""" + alert_data = { + "rule": rule.name, + "severity": rule.severity, + "value": metrics[rule.condition], + "threshold": rule.threshold, + "timestamp": datetime.now().isoformat(), + "environment": self.config.environment, + "service": self.config.service + } + + # Send to all channels + tasks = [] + for channel_name in rule.channels: + if channel_name in self.channels: + channel = self.channels[channel_name] + tasks.append(channel.send(alert_data)) + + await asyncio.gather(*tasks) + + # Update alert history + self.alert_history[rule.name] = datetime.now() + +# Alert channels +class SlackAlertChannel: + def __init__(self, webhook_url): + self.webhook_url = webhook_url + + async def send(self, alert_data): + """Send alert to Slack""" + color = { + "critical": "danger", + "warning": "warning", + "info": "good" + }.get(alert_data["severity"], "danger") + + payload = { + "attachments": [{ + "color": color, + "title": f"🚨 {alert_data['rule']}", + "fields": [ + { + "title": "Severity", + "value": alert_data["severity"].upper(), + "short": True + }, + { + "title": "Environment", + "value": alert_data["environment"], + "short": True + }, + { + "title": "Current Value", + "value": str(alert_data["value"]), + "short": True + }, + { + "title": "Threshold", + "value": str(alert_data["threshold"]), + "short": True + } + ], + "footer": alert_data["service"], + "ts": int(datetime.now().timestamp()) + }] + } + + # Send to Slack + async with aiohttp.ClientSession() as session: + await session.post(self.webhook_url, json=payload) +``` + +### 5. Error Grouping and Deduplication + +Implement intelligent error grouping: + +**Error Grouping Algorithm** +```python +import hashlib +import re +from difflib import SequenceMatcher + +class ErrorGrouper: + def __init__(self): + self.groups = {} + self.patterns = self._compile_patterns() + + def _compile_patterns(self): + """Compile regex patterns for normalization""" + return { + 'numbers': re.compile(r'\b\d+\b'), + 'uuids': re.compile(r'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'), + 'urls': re.compile(r'https?://[^\s]+'), + 'file_paths': re.compile(r'(/[^/\s]+)+'), + 'memory_addresses': re.compile(r'0x[0-9a-fA-F]+'), + 'timestamps': re.compile(r'\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}') + } + + def group_error(self, error): + """Group error with similar errors""" + fingerprint = self.generate_fingerprint(error) + + # Find existing group + group = self.find_similar_group(fingerprint, error) + + if group: + group['count'] += 1 + group['last_seen'] = error['timestamp'] + group['instances'].append(error) + else: + # Create new group + self.groups[fingerprint] = { + 'fingerprint': fingerprint, + 'first_seen': error['timestamp'], + 'last_seen': error['timestamp'], + 'count': 1, + 'instances': [error], + 'pattern': self.extract_pattern(error) + } + + return fingerprint + + def generate_fingerprint(self, error): + """Generate unique fingerprint for error""" + # Normalize error message + normalized = self.normalize_message(error['message']) + + # Include error type and location + components = [ + error.get('type', 'Unknown'), + normalized, + self.extract_location(error.get('stack', '')) + ] + + # Generate hash + fingerprint = hashlib.sha256( + '|'.join(components).encode() + ).hexdigest()[:16] + + return fingerprint + + def normalize_message(self, message): + """Normalize error message for grouping""" + # Replace dynamic values + normalized = message + for pattern_name, pattern in self.patterns.items(): + normalized = pattern.sub(f'<{pattern_name}>', normalized) + + return normalized.strip() + + def extract_location(self, stack): + """Extract error location from stack trace""" + if not stack: + return 'unknown' + + lines = stack.split('\n') + for line in lines: + # Look for file references + if ' at ' in line: + # Extract file and line number + match = re.search(r'at\s+(.+?)\s*\((.+?):(\d+):(\d+)\)', line) + if match: + file_path = match.group(2) + # Normalize file path + file_path = re.sub(r'.*/(?=src/|lib/|app/)', '', file_path) + return f"{file_path}:{match.group(3)}" + + return 'unknown' + + def find_similar_group(self, fingerprint, error): + """Find similar error group using fuzzy matching""" + if fingerprint in self.groups: + return self.groups[fingerprint] + + # Try fuzzy matching + normalized_message = self.normalize_message(error['message']) + + for group_fp, group in self.groups.items(): + similarity = SequenceMatcher( + None, + normalized_message, + group['pattern'] + ).ratio() + + if similarity > 0.85: # 85% similarity threshold + return group + + return None +``` + +### 6. Performance Impact Tracking + +Monitor performance impact of errors: + +**Performance Monitor** +```typescript +// performance-monitor.ts +interface PerformanceMetrics { + responseTime: number; + errorRate: number; + throughput: number; + apdex: number; + resourceUsage: { + cpu: number; + memory: number; + disk: number; + }; +} + +class PerformanceMonitor { + private metrics: Map = new Map(); + private intervals: Map = new Map(); + + startMonitoring(service: string, interval: number = 60000) { + const timer = setInterval(() => { + this.collectMetrics(service); + }, interval); + + this.intervals.set(service, timer); + } + + private async collectMetrics(service: string) { + const metrics: PerformanceMetrics = { + responseTime: await this.getResponseTime(service), + errorRate: await this.getErrorRate(service), + throughput: await this.getThroughput(service), + apdex: await this.calculateApdex(service), + resourceUsage: await this.getResourceUsage() + }; + + // Store metrics + if (!this.metrics.has(service)) { + this.metrics.set(service, []); + } + + const serviceMetrics = this.metrics.get(service)!; + serviceMetrics.push(metrics); + + // Keep only last 24 hours + const dayAgo = Date.now() - 24 * 60 * 60 * 1000; + const filtered = serviceMetrics.filter(m => m.timestamp > dayAgo); + this.metrics.set(service, filtered); + + // Check for anomalies + this.detectAnomalies(service, metrics); + } + + private detectAnomalies(service: string, current: PerformanceMetrics) { + const history = this.metrics.get(service) || []; + if (history.length < 10) return; // Need history for comparison + + // Calculate baselines + const baseline = this.calculateBaseline(history.slice(-60)); // Last hour + + // Check for anomalies + const anomalies = []; + + if (current.responseTime > baseline.responseTime * 2) { + anomalies.push({ + type: 'response_time_spike', + severity: 'warning', + value: current.responseTime, + baseline: baseline.responseTime + }); + } + + if (current.errorRate > baseline.errorRate + 0.05) { + anomalies.push({ + type: 'error_rate_increase', + severity: 'critical', + value: current.errorRate, + baseline: baseline.errorRate + }); + } + + if (anomalies.length > 0) { + this.reportAnomalies(service, anomalies); + } + } + + private calculateBaseline(history: PerformanceMetrics[]) { + const sum = history.reduce((acc, m) => ({ + responseTime: acc.responseTime + m.responseTime, + errorRate: acc.errorRate + m.errorRate, + throughput: acc.throughput + m.throughput, + apdex: acc.apdex + m.apdex + }), { + responseTime: 0, + errorRate: 0, + throughput: 0, + apdex: 0 + }); + + return { + responseTime: sum.responseTime / history.length, + errorRate: sum.errorRate / history.length, + throughput: sum.throughput / history.length, + apdex: sum.apdex / history.length + }; + } + + async calculateApdex(service: string, threshold: number = 500) { + // Apdex = (Satisfied + Tolerating/2) / Total + const satisfied = await this.countRequests(service, 0, threshold); + const tolerating = await this.countRequests(service, threshold, threshold * 4); + const total = await this.getTotalRequests(service); + + if (total === 0) return 1; + + return (satisfied + tolerating / 2) / total; + } +} +``` + +### 7. Error Recovery Strategies + +Implement automatic error recovery: + +**Recovery Manager** +```javascript +// recovery-manager.js +class RecoveryManager { + constructor(config) { + this.strategies = new Map(); + this.retryPolicies = config.retryPolicies || {}; + this.circuitBreakers = new Map(); + this.registerDefaultStrategies(); + } + + registerStrategy(errorType, strategy) { + this.strategies.set(errorType, strategy); + } + + registerDefaultStrategies() { + // Network errors + this.registerStrategy('NetworkError', async (error, context) => { + return this.retryWithBackoff( + context.operation, + this.retryPolicies.network || { + maxRetries: 3, + baseDelay: 1000, + maxDelay: 10000 + } + ); + }); + + // Database errors + this.registerStrategy('DatabaseError', async (error, context) => { + // Try read replica if available + if (context.operation.type === 'read' && context.readReplicas) { + return this.tryReadReplica(context); + } + + // Otherwise retry with backoff + return this.retryWithBackoff( + context.operation, + this.retryPolicies.database || { + maxRetries: 2, + baseDelay: 500, + maxDelay: 5000 + } + ); + }); + + // Rate limit errors + this.registerStrategy('RateLimitError', async (error, context) => { + const retryAfter = error.retryAfter || 60; + await this.delay(retryAfter * 1000); + return context.operation(); + }); + + // Circuit breaker for external services + this.registerStrategy('ExternalServiceError', async (error, context) => { + const breaker = this.getCircuitBreaker(context.service); + + try { + return await breaker.execute(context.operation); + } catch (error) { + // Fallback to cache or default + if (context.fallback) { + return context.fallback(); + } + throw error; + } + }); + } + + async recover(error, context) { + const errorType = this.classifyError(error); + const strategy = this.strategies.get(errorType); + + if (!strategy) { + // No recovery strategy, rethrow + throw error; + } + + try { + const result = await strategy(error, context); + + // Log recovery success + this.logRecovery(error, errorType, 'success'); + + return result; + } catch (recoveryError) { + // Log recovery failure + this.logRecovery(error, errorType, 'failure', recoveryError); + + // Throw original error + throw error; + } + } + + async retryWithBackoff(operation, policy) { + let lastError; + let delay = policy.baseDelay; + + for (let attempt = 0; attempt < policy.maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error; + + if (attempt < policy.maxRetries - 1) { + await this.delay(delay); + delay = Math.min(delay * 2, policy.maxDelay); + } + } + } + + throw lastError; + } + + getCircuitBreaker(service) { + if (!this.circuitBreakers.has(service)) { + this.circuitBreakers.set(service, new CircuitBreaker({ + timeout: 3000, + errorThresholdPercentage: 50, + resetTimeout: 30000, + rollingCountTimeout: 10000, + rollingCountBuckets: 10, + volumeThreshold: 10 + })); + } + + return this.circuitBreakers.get(service); + } + + classifyError(error) { + // Classify by error code + if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') { + return 'NetworkError'; + } + + if (error.code === 'ER_LOCK_DEADLOCK' || error.code === 'SQLITE_BUSY') { + return 'DatabaseError'; + } + + if (error.status === 429) { + return 'RateLimitError'; + } + + if (error.isExternalService) { + return 'ExternalServiceError'; + } + + // Default + return 'UnknownError'; + } +} + +// Circuit breaker implementation +class CircuitBreaker { + constructor(options) { + this.options = options; + this.state = 'CLOSED'; + this.failures = 0; + this.successes = 0; + this.nextAttempt = Date.now(); + } + + async execute(operation) { + if (this.state === 'OPEN') { + if (Date.now() < this.nextAttempt) { + throw new Error('Circuit breaker is OPEN'); + } + + // Try half-open + this.state = 'HALF_OPEN'; + } + + try { + const result = await Promise.race([ + operation(), + this.timeout(this.options.timeout) + ]); + + this.onSuccess(); + return result; + } catch (error) { + this.onFailure(); + throw error; + } + } + + onSuccess() { + this.failures = 0; + + if (this.state === 'HALF_OPEN') { + this.successes++; + if (this.successes >= this.options.volumeThreshold) { + this.state = 'CLOSED'; + this.successes = 0; + } + } + } + + onFailure() { + this.failures++; + + if (this.state === 'HALF_OPEN') { + this.state = 'OPEN'; + this.nextAttempt = Date.now() + this.options.resetTimeout; + } else if (this.failures >= this.options.volumeThreshold) { + this.state = 'OPEN'; + this.nextAttempt = Date.now() + this.options.resetTimeout; + } + } +} +``` + +### 8. Error Dashboard + +Create comprehensive error dashboard: + +**Dashboard Component** +```typescript +// error-dashboard.tsx +import React from 'react'; +import { LineChart, BarChart, PieChart } from 'recharts'; + +const ErrorDashboard: React.FC = () => { + const [metrics, setMetrics] = useState(); + const [timeRange, setTimeRange] = useState('1h'); + + useEffect(() => { + const fetchMetrics = async () => { + const data = await getErrorMetrics(timeRange); + setMetrics(data); + }; + + fetchMetrics(); + const interval = setInterval(fetchMetrics, 30000); // Update every 30s + + return () => clearInterval(interval); + }, [timeRange]); + + if (!metrics) return ; + + return ( +
+
+

Error Tracking Dashboard

+ +
+ + + 0.05 ? 'critical' : 'ok'} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Recent Errors

+ +
+ + +

Active Alerts

+ +
+
+ ); +}; + +// Real-time error stream +const ErrorStream: React.FC = () => { + const [errors, setErrors] = useState([]); + + useEffect(() => { + const eventSource = new EventSource('/api/errors/stream'); + + eventSource.onmessage = (event) => { + const error = JSON.parse(event.data); + setErrors(prev => [error, ...prev].slice(0, 100)); + }; + + return () => eventSource.close(); + }, []); + + return ( +
+

Live Error Stream

+
+ {errors.map((error, index) => ( + + ))} +
+
+ ); +}; +``` + +## Output Format + +1. **Error Tracking Analysis**: Current error handling assessment +2. **Integration Configuration**: Setup for error tracking services +3. **Logging Implementation**: Structured logging setup +4. **Alert Rules**: Intelligent alerting configuration +5. **Error Grouping**: Deduplication and grouping logic +6. **Recovery Strategies**: Automatic error recovery implementation +7. **Dashboard Setup**: Real-time error monitoring dashboard +8. **Documentation**: Implementation and troubleshooting guide + +Focus on providing comprehensive error visibility, intelligent alerting, and quick error resolution capabilities. \ No newline at end of file diff --git a/commands/multi-agent-review.md b/commands/multi-agent-review.md new file mode 100644 index 0000000..8b37727 --- /dev/null +++ b/commands/multi-agent-review.md @@ -0,0 +1,194 @@ +# Multi-Agent Code Review Orchestration Tool + +## Role: Expert Multi-Agent Review Orchestration Specialist + +A sophisticated AI-powered code review system designed to provide comprehensive, multi-perspective analysis of software artifacts through intelligent agent coordination and specialized domain expertise. + +## Context and Purpose + +The Multi-Agent Review Tool leverages a distributed, specialized agent network to perform holistic code assessments that transcend traditional single-perspective review approaches. By coordinating agents with distinct expertise, we generate a comprehensive evaluation that captures nuanced insights across multiple critical dimensions: + +- **Depth**: Specialized agents dive deep into specific domains +- **Breadth**: Parallel processing enables comprehensive coverage +- **Intelligence**: Context-aware routing and intelligent synthesis +- **Adaptability**: Dynamic agent selection based on code characteristics + +## Tool Arguments and Configuration + +### Input Parameters +- `$ARGUMENTS`: Target code/project for review + - Supports: File paths, Git repositories, code snippets + - Handles multiple input formats + - Enables context extraction and agent routing + +### Agent Types +1. Code Quality Reviewers +2. Security Auditors +3. Architecture Specialists +4. Performance Analysts +5. Compliance Validators +6. Best Practices Experts + +## Multi-Agent Coordination Strategy + +### 1. Agent Selection and Routing Logic +- **Dynamic Agent Matching**: + - Analyze input characteristics + - Select most appropriate agent types + - Configure specialized sub-agents dynamically +- **Expertise Routing**: + ```python + def route_agents(code_context): + agents = [] + if is_web_application(code_context): + agents.extend([ + "security-auditor", + "web-architecture-reviewer" + ]) + if is_performance_critical(code_context): + agents.append("performance-analyst") + return agents + ``` + +### 2. Context Management and State Passing +- **Contextual Intelligence**: + - Maintain shared context across agent interactions + - Pass refined insights between agents + - Support incremental review refinement +- **Context Propagation Model**: + ```python + class ReviewContext: + def __init__(self, target, metadata): + self.target = target + self.metadata = metadata + self.agent_insights = {} + + def update_insights(self, agent_type, insights): + self.agent_insights[agent_type] = insights + ``` + +### 3. Parallel vs Sequential Execution +- **Hybrid Execution Strategy**: + - Parallel execution for independent reviews + - Sequential processing for dependent insights + - Intelligent timeout and fallback mechanisms +- **Execution Flow**: + ```python + def execute_review(review_context): + # Parallel independent agents + parallel_agents = [ + "code-quality-reviewer", + "security-auditor" + ] + + # Sequential dependent agents + sequential_agents = [ + "architecture-reviewer", + "performance-optimizer" + ] + ``` + +### 4. Result Aggregation and Synthesis +- **Intelligent Consolidation**: + - Merge insights from multiple agents + - Resolve conflicting recommendations + - Generate unified, prioritized report +- **Synthesis Algorithm**: + ```python + def synthesize_review_insights(agent_results): + consolidated_report = { + "critical_issues": [], + "important_issues": [], + "improvement_suggestions": [] + } + # Intelligent merging logic + return consolidated_report + ``` + +### 5. Conflict Resolution Mechanism +- **Smart Conflict Handling**: + - Detect contradictory agent recommendations + - Apply weighted scoring + - Escalate complex conflicts +- **Resolution Strategy**: + ```python + def resolve_conflicts(agent_insights): + conflict_resolver = ConflictResolutionEngine() + return conflict_resolver.process(agent_insights) + ``` + +### 6. Performance Optimization +- **Efficiency Techniques**: + - Minimal redundant processing + - Cached intermediate results + - Adaptive agent resource allocation +- **Optimization Approach**: + ```python + def optimize_review_process(review_context): + return ReviewOptimizer.allocate_resources(review_context) + ``` + +### 7. Quality Validation Framework +- **Comprehensive Validation**: + - Cross-agent result verification + - Statistical confidence scoring + - Continuous learning and improvement +- **Validation Process**: + ```python + def validate_review_quality(review_results): + quality_score = QualityScoreCalculator.compute(review_results) + return quality_score > QUALITY_THRESHOLD + ``` + +## Example Implementations + +### 1. Parallel Code Review Scenario +```python +multi_agent_review( + target="/path/to/project", + agents=[ + {"type": "security-auditor", "weight": 0.3}, + {"type": "architecture-reviewer", "weight": 0.3}, + {"type": "performance-analyst", "weight": 0.2} + ] +) +``` + +### 2. Sequential Workflow +```python +sequential_review_workflow = [ + {"phase": "design-review", "agent": "architect-reviewer"}, + {"phase": "implementation-review", "agent": "code-quality-reviewer"}, + {"phase": "testing-review", "agent": "test-coverage-analyst"}, + {"phase": "deployment-readiness", "agent": "devops-validator"} +] +``` + +### 3. Hybrid Orchestration +```python +hybrid_review_strategy = { + "parallel_agents": ["security", "performance"], + "sequential_agents": ["architecture", "compliance"] +} +``` + +## Reference Implementations + +1. **Web Application Security Review** +2. **Microservices Architecture Validation** + +## Best Practices and Considerations + +- Maintain agent independence +- Implement robust error handling +- Use probabilistic routing +- Support incremental reviews +- Ensure privacy and security + +## Extensibility + +The tool is designed with a plugin-based architecture, allowing easy addition of new agent types and review strategies. + +## Invocation + +Target for review: $ARGUMENTS \ No newline at end of file diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..964d63b --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,61 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:HermeticOrmus/Alqvimia-Contador:plugins/error-debugging", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "be21c84141ab6ee8b8ec8df618ff4545005cadc2", + "treeHash": "ed376be6a717b567efa3ef9a6a031817ea01c86bdebf6f8621d9ca1fabb60d01", + "generatedAt": "2025-11-28T10:10:35.288764Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "error-debugging", + "description": "Error analysis, trace debugging, and multi-agent problem diagnosis", + "version": "1.2.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "7bfce75a2dcfdbda521528b0b33045f7a6f6df34c4a7d3df90228ef4dde1a50e" + }, + { + "path": "agents/debugger.md", + "sha256": "2d74eefa19d8ca12e22fadf3f58e4fe45114962ba027d9cc5a8495e2acd86d93" + }, + { + "path": "agents/error-detective.md", + "sha256": "8574cc752979da28d8242167f4ab92f0ecd6a5429f260259e1219cc3a1afed8d" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "54397331d0902c24c20238ebdb97c02f886c676ae12e8ab3c25a9b7c74b86c09" + }, + { + "path": "commands/error-trace.md", + "sha256": "d05ec7e920d33f5fbe7e82f8889ebdccf5af613b02b6b5d77ad6d48f2a09674f" + }, + { + "path": "commands/multi-agent-review.md", + "sha256": "bf33bcd91fb4a2d10ad9ad2a7e6e90b37e14db2d7b3fd9e436b9adc798b87143" + }, + { + "path": "commands/error-analysis.md", + "sha256": "9e8f3cd0b0bd43c2a6c9f599037374d2061187ff3ed418cd4c72dfcd9b27de3f" + } + ], + "dirSha256": "ed376be6a717b567efa3ef9a6a031817ea01c86bdebf6f8621d9ca1fabb60d01" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file