claude-mpm 4.17.0__py3-none-any.whl → 4.18.0__py3-none-any.whl

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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (47) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +48 -17
  3. claude_mpm/agents/agent_loader.py +4 -4
  4. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  5. claude_mpm/config/agent_config.py +2 -2
  6. claude_mpm/core/factories.py +1 -1
  7. claude_mpm/core/optimized_agent_loader.py +3 -3
  8. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  9. claude_mpm/models/resume_log.py +340 -0
  10. claude_mpm/services/agents/auto_config_manager.py +1 -1
  11. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  12. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  13. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  14. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  15. claude_mpm/services/agents/local_template_manager.py +1 -1
  16. claude_mpm/services/core/path_resolver.py +1 -1
  17. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  18. claude_mpm/services/mcp_config_manager.py +2 -2
  19. claude_mpm/services/session_manager.py +205 -1
  20. claude_mpm/services/unified/deployment_strategies/local.py +1 -1
  21. claude_mpm/skills/bundled/api-documentation.md +393 -0
  22. claude_mpm/skills/bundled/async-testing.md +571 -0
  23. claude_mpm/skills/bundled/code-review.md +143 -0
  24. claude_mpm/skills/bundled/database-migration.md +199 -0
  25. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  26. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  27. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  28. claude_mpm/skills/bundled/git-workflow.md +414 -0
  29. claude_mpm/skills/bundled/imagemagick.md +204 -0
  30. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  31. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  32. claude_mpm/skills/bundled/pdf.md +141 -0
  33. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  34. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  35. claude_mpm/skills/bundled/security-scanning.md +327 -0
  36. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  37. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  38. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  39. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  40. claude_mpm/skills/bundled/xlsx.md +157 -0
  41. claude_mpm/utils/agent_dependency_loader.py +2 -2
  42. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/METADATA +68 -1
  43. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/RECORD +47 -24
  44. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/WHEEL +0 -0
  45. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/entry_points.txt +0 -0
  46. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/licenses/LICENSE +0 -0
  47. {claude_mpm-4.17.0.dist-info → claude_mpm-4.18.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,571 @@
1
+ ---
2
+ skill_id: async-testing
3
+ skill_version: 0.1.0
4
+ description: Patterns for testing asynchronous code across languages, eliminating redundant async testing guidance per agent.
5
+ updated_at: 2025-10-30T17:00:00Z
6
+ tags: [testing, async, asynchronous, concurrency]
7
+ ---
8
+
9
+ # Async Testing
10
+
11
+ Patterns for testing asynchronous code across languages. Eliminates ~200-300 lines of redundant async testing guidance per agent.
12
+
13
+ ## Core Async Testing Principles
14
+
15
+ ### 1. Async Code is Still Testable
16
+
17
+ Asynchronous operations can and should be tested just like synchronous code. The key is understanding the execution model.
18
+
19
+ ### 2. Control Time in Tests
20
+
21
+ Never rely on actual timeouts in tests. Use time mocking for deterministic, fast tests.
22
+
23
+ ### 3. Test Race Conditions Explicitly
24
+
25
+ Concurrent code has race conditions. Test them deliberately rather than hoping they don't happen.
26
+
27
+ ## Language-Specific Patterns
28
+
29
+ ### Python (asyncio)
30
+
31
+ #### Basic Async Test Setup
32
+
33
+ ```python
34
+ import pytest
35
+ import asyncio
36
+
37
+ # Mark test as async
38
+ @pytest.mark.asyncio
39
+ async def test_async_operation():
40
+ result = await async_function()
41
+ assert result == expected_value
42
+
43
+ # Alternative: Use asyncio.run()
44
+ def test_async_operation_sync():
45
+ result = asyncio.run(async_function())
46
+ assert result == expected_value
47
+ ```
48
+
49
+ #### Testing Async Fixtures
50
+
51
+ ```python
52
+ @pytest.fixture
53
+ async def async_database():
54
+ """Async fixture for database setup/teardown."""
55
+ db = await create_async_database()
56
+ yield db
57
+ await db.close()
58
+
59
+ @pytest.mark.asyncio
60
+ async def test_with_async_fixture(async_database):
61
+ result = await async_database.query("SELECT * FROM users")
62
+ assert len(result) > 0
63
+ ```
64
+
65
+ #### Testing Concurrent Operations
66
+
67
+ ```python
68
+ @pytest.mark.asyncio
69
+ async def test_concurrent_requests():
70
+ # Run multiple async operations concurrently
71
+ results = await asyncio.gather(
72
+ fetch_user(1),
73
+ fetch_user(2),
74
+ fetch_user(3)
75
+ )
76
+
77
+ assert len(results) == 3
78
+ assert all(user.is_valid() for user in results)
79
+ ```
80
+
81
+ #### Testing Timeouts
82
+
83
+ ```python
84
+ @pytest.mark.asyncio
85
+ async def test_operation_timeout():
86
+ with pytest.raises(asyncio.TimeoutError):
87
+ await asyncio.wait_for(slow_operation(), timeout=1.0)
88
+ ```
89
+
90
+ #### Mocking Async Functions
91
+
92
+ ```python
93
+ from unittest.mock import AsyncMock, patch
94
+
95
+ @pytest.mark.asyncio
96
+ async def test_with_async_mock():
97
+ mock_api = AsyncMock(return_value={"status": "success"})
98
+
99
+ with patch('module.api_call', mock_api):
100
+ result = await function_that_calls_api()
101
+
102
+ assert result["status"] == "success"
103
+ mock_api.assert_called_once()
104
+ ```
105
+
106
+ ### JavaScript/TypeScript (async/await)
107
+
108
+ #### Basic Async Test Setup
109
+
110
+ ```javascript
111
+ // Jest
112
+ describe('Async Operations', () => {
113
+ test('should handle async operation', async () => {
114
+ const result = await asyncFunction();
115
+ expect(result).toBe(expectedValue);
116
+ });
117
+
118
+ // Alternative: Return promise
119
+ test('should handle promise', () => {
120
+ return asyncFunction().then(result => {
121
+ expect(result).toBe(expectedValue);
122
+ });
123
+ });
124
+ });
125
+ ```
126
+
127
+ #### Testing Promise Resolution/Rejection
128
+
129
+ ```javascript
130
+ test('should resolve with correct data', async () => {
131
+ await expect(fetchUser(1)).resolves.toEqual({
132
+ id: 1,
133
+ name: 'John'
134
+ });
135
+ });
136
+
137
+ test('should reject when user not found', async () => {
138
+ await expect(fetchUser(999)).rejects.toThrow('User not found');
139
+ });
140
+ ```
141
+
142
+ #### Testing Concurrent Operations
143
+
144
+ ```javascript
145
+ test('should handle multiple concurrent requests', async () => {
146
+ const promises = [
147
+ fetchUser(1),
148
+ fetchUser(2),
149
+ fetchUser(3)
150
+ ];
151
+
152
+ const results = await Promise.all(promises);
153
+
154
+ expect(results).toHaveLength(3);
155
+ expect(results.every(user => user.id > 0)).toBe(true);
156
+ });
157
+ ```
158
+
159
+ #### Testing Race Conditions
160
+
161
+ ```javascript
162
+ test('should handle race condition correctly', async () => {
163
+ let counter = 0;
164
+ const increment = async () => {
165
+ const current = counter;
166
+ await delay(10); // Simulate async work
167
+ counter = current + 1;
168
+ };
169
+
170
+ // Run concurrently - will show race condition
171
+ await Promise.all([increment(), increment(), increment()]);
172
+
173
+ // Without proper synchronization, counter might be 1 instead of 3
174
+ expect(counter).toBe(3); // This test will fail if race condition exists
175
+ });
176
+ ```
177
+
178
+ #### Mocking Async Functions
179
+
180
+ ```javascript
181
+ jest.mock('./api');
182
+
183
+ test('should use mocked async function', async () => {
184
+ const mockFetchUser = require('./api').fetchUser;
185
+ mockFetchUser.mockResolvedValue({ id: 1, name: 'John' });
186
+
187
+ const result = await getUserData(1);
188
+
189
+ expect(result.name).toBe('John');
190
+ expect(mockFetchUser).toHaveBeenCalledWith(1);
191
+ });
192
+ ```
193
+
194
+ #### Testing Timeouts
195
+
196
+ ```javascript
197
+ test('should timeout long operations', async () => {
198
+ const timeoutPromise = new Promise((_, reject) =>
199
+ setTimeout(() => reject(new Error('Timeout')), 1000)
200
+ );
201
+
202
+ await expect(
203
+ Promise.race([slowOperation(), timeoutPromise])
204
+ ).rejects.toThrow('Timeout');
205
+ });
206
+ ```
207
+
208
+ ### Go (goroutines)
209
+
210
+ #### Basic Async Test Setup
211
+
212
+ ```go
213
+ func TestAsyncOperation(t *testing.T) {
214
+ done := make(chan bool)
215
+ var result int
216
+
217
+ go func() {
218
+ result = expensiveOperation()
219
+ done <- true
220
+ }()
221
+
222
+ <-done // Wait for completion
223
+
224
+ if result != expected {
225
+ t.Errorf("Expected %d, got %d", expected, result)
226
+ }
227
+ }
228
+ ```
229
+
230
+ #### Testing with Timeouts
231
+
232
+ ```go
233
+ func TestWithTimeout(t *testing.T) {
234
+ done := make(chan bool)
235
+
236
+ go func() {
237
+ slowOperation()
238
+ done <- true
239
+ }()
240
+
241
+ select {
242
+ case <-done:
243
+ // Success
244
+ case <-time.After(1 * time.Second):
245
+ t.Fatal("Operation timed out")
246
+ }
247
+ }
248
+ ```
249
+
250
+ #### Testing Concurrent Operations
251
+
252
+ ```go
253
+ func TestConcurrentOperations(t *testing.T) {
254
+ const numWorkers = 10
255
+ results := make(chan int, numWorkers)
256
+
257
+ for i := 0; i < numWorkers; i++ {
258
+ go func(id int) {
259
+ results <- processTask(id)
260
+ }(i)
261
+ }
262
+
263
+ // Collect results
264
+ var sum int
265
+ for i := 0; i < numWorkers; i++ {
266
+ sum += <-results
267
+ }
268
+
269
+ if sum != expectedSum {
270
+ t.Errorf("Expected sum %d, got %d", expectedSum, sum)
271
+ }
272
+ }
273
+ ```
274
+
275
+ #### Testing Race Conditions
276
+
277
+ ```go
278
+ func TestRaceCondition(t *testing.T) {
279
+ // Enable race detector: go test -race
280
+ counter := 0
281
+ done := make(chan bool)
282
+
283
+ for i := 0; i < 100; i++ {
284
+ go func() {
285
+ counter++ // Race condition!
286
+ done <- true
287
+ }()
288
+ }
289
+
290
+ for i := 0; i < 100; i++ {
291
+ <-done
292
+ }
293
+
294
+ // With race condition, counter might be < 100
295
+ if counter != 100 {
296
+ t.Errorf("Expected 100, got %d (race condition detected)", counter)
297
+ }
298
+ }
299
+ ```
300
+
301
+ ### Rust (async/await with tokio)
302
+
303
+ #### Basic Async Test Setup
304
+
305
+ ```rust
306
+ #[tokio::test]
307
+ async fn test_async_operation() {
308
+ let result = async_function().await;
309
+ assert_eq!(result, expected_value);
310
+ }
311
+
312
+ // Multi-threaded runtime
313
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
314
+ async fn test_concurrent_operation() {
315
+ let result = concurrent_operation().await;
316
+ assert!(result.is_ok());
317
+ }
318
+ ```
319
+
320
+ #### Testing with Timeouts
321
+
322
+ ```rust
323
+ use tokio::time::{timeout, Duration};
324
+
325
+ #[tokio::test]
326
+ async fn test_with_timeout() {
327
+ let result = timeout(
328
+ Duration::from_secs(1),
329
+ slow_operation()
330
+ ).await;
331
+
332
+ assert!(result.is_err(), "Operation should have timed out");
333
+ }
334
+ ```
335
+
336
+ #### Testing Concurrent Operations
337
+
338
+ ```rust
339
+ #[tokio::test]
340
+ async fn test_concurrent_tasks() {
341
+ let task1 = tokio::spawn(async { fetch_data(1).await });
342
+ let task2 = tokio::spawn(async { fetch_data(2).await });
343
+ let task3 = tokio::spawn(async { fetch_data(3).await });
344
+
345
+ let results = tokio::try_join!(task1, task2, task3).unwrap();
346
+
347
+ assert_eq!(results.0, expected1);
348
+ assert_eq!(results.1, expected2);
349
+ assert_eq!(results.2, expected3);
350
+ }
351
+ ```
352
+
353
+ ## Common Async Testing Patterns
354
+
355
+ ### Pattern 1: Testing Callback-Based Async
356
+
357
+ ```javascript
358
+ // Converting callbacks to promises for testing
359
+ function promisify(callbackFn) {
360
+ return (...args) => {
361
+ return new Promise((resolve, reject) => {
362
+ callbackFn(...args, (err, result) => {
363
+ if (err) reject(err);
364
+ else resolve(result);
365
+ });
366
+ });
367
+ };
368
+ }
369
+
370
+ test('should handle callback-based async', async () => {
371
+ const asyncFn = promisify(callbackBasedFunction);
372
+ const result = await asyncFn(arg1, arg2);
373
+ expect(result).toBe(expected);
374
+ });
375
+ ```
376
+
377
+ ### Pattern 2: Testing Event Emitters
378
+
379
+ ```javascript
380
+ test('should emit events in correct order', async () => {
381
+ const events = [];
382
+ const emitter = new EventEmitter();
383
+
384
+ emitter.on('start', () => events.push('start'));
385
+ emitter.on('process', () => events.push('process'));
386
+ emitter.on('complete', () => events.push('complete'));
387
+
388
+ await performAsyncOperation(emitter);
389
+
390
+ expect(events).toEqual(['start', 'process', 'complete']);
391
+ });
392
+ ```
393
+
394
+ ### Pattern 3: Testing Retry Logic
395
+
396
+ ```javascript
397
+ test('should retry failed operations', async () => {
398
+ let attempts = 0;
399
+ const unreliableOperation = async () => {
400
+ attempts++;
401
+ if (attempts < 3) throw new Error('Temporary failure');
402
+ return 'success';
403
+ };
404
+
405
+ const result = await retryOperation(unreliableOperation, 3);
406
+
407
+ expect(result).toBe('success');
408
+ expect(attempts).toBe(3);
409
+ });
410
+ ```
411
+
412
+ ### Pattern 4: Testing Debouncing/Throttling
413
+
414
+ ```javascript
415
+ test('should debounce rapid calls', async () => {
416
+ let callCount = 0;
417
+ const debouncedFn = debounce(() => callCount++, 100);
418
+
419
+ // Rapid calls
420
+ debouncedFn();
421
+ debouncedFn();
422
+ debouncedFn();
423
+
424
+ // Wait for debounce period
425
+ await delay(150);
426
+
427
+ expect(callCount).toBe(1); // Only called once
428
+ });
429
+ ```
430
+
431
+ ## Testing Async State Management
432
+
433
+ ### Testing Loading States
434
+
435
+ ```javascript
436
+ test('should show loading state during async operation', async () => {
437
+ const component = render(<AsyncComponent />);
438
+
439
+ // Initial state
440
+ expect(component.getByText('Loading...')).toBeInTheDocument();
441
+
442
+ // Wait for async operation
443
+ await waitFor(() => {
444
+ expect(component.getByText('Data loaded')).toBeInTheDocument();
445
+ });
446
+
447
+ expect(component.queryByText('Loading...')).not.toBeInTheDocument();
448
+ });
449
+ ```
450
+
451
+ ### Testing Error States
452
+
453
+ ```javascript
454
+ test('should show error state on failure', async () => {
455
+ // Mock API to fail
456
+ mockApi.fetchData.mockRejectedValue(new Error('Network error'));
457
+
458
+ const component = render(<AsyncComponent />);
459
+
460
+ await waitFor(() => {
461
+ expect(component.getByText('Error: Network error')).toBeInTheDocument();
462
+ });
463
+ });
464
+ ```
465
+
466
+ ## Best Practices
467
+
468
+ ### ✅ DO: Control Time in Tests
469
+
470
+ ```javascript
471
+ // Good: Mock timers for deterministic tests
472
+ jest.useFakeTimers();
473
+
474
+ test('should execute after delay', () => {
475
+ const callback = jest.fn();
476
+ setTimeout(callback, 1000);
477
+
478
+ jest.advanceTimersByTime(1000);
479
+
480
+ expect(callback).toHaveBeenCalled();
481
+ });
482
+ ```
483
+
484
+ ### ✅ DO: Test Both Success and Failure Paths
485
+
486
+ ```python
487
+ @pytest.mark.asyncio
488
+ async def test_success_path():
489
+ result = await operation_that_succeeds()
490
+ assert result.is_success()
491
+
492
+ @pytest.mark.asyncio
493
+ async def test_failure_path():
494
+ with pytest.raises(OperationError):
495
+ await operation_that_fails()
496
+ ```
497
+
498
+ ### ✅ DO: Use Appropriate Timeouts
499
+
500
+ ```python
501
+ # Good: Reasonable timeout for test
502
+ @pytest.mark.asyncio
503
+ async def test_with_timeout():
504
+ async with timeout(5): # 5 seconds is reasonable
505
+ result = await long_operation()
506
+ ```
507
+
508
+ ### ❌ DON'T: Use Real Delays in Tests
509
+
510
+ ```python
511
+ # Bad: Real delays make tests slow
512
+ await asyncio.sleep(5) # Don't do this!
513
+
514
+ # Good: Mock time or use smaller delays for testing
515
+ with patch('asyncio.sleep'):
516
+ await operation_with_delay()
517
+ ```
518
+
519
+ ### ❌ DON'T: Forget to Await
520
+
521
+ ```python
522
+ # Bad: Forgot to await
523
+ def test_async_wrong():
524
+ result = async_function() # Returns coroutine, doesn't execute!
525
+ assert result == expected # Will fail!
526
+
527
+ # Good: Properly await
528
+ @pytest.mark.asyncio
529
+ async def test_async_correct():
530
+ result = await async_function()
531
+ assert result == expected
532
+ ```
533
+
534
+ ### ❌ DON'T: Ignore Unhandled Promise Rejections
535
+
536
+ ```javascript
537
+ // Bad: Unhandled rejection
538
+ test('bad test', async () => {
539
+ asyncOperation(); // Promise rejection not handled!
540
+ });
541
+
542
+ // Good: Handle all promises
543
+ test('good test', async () => {
544
+ await expect(asyncOperation()).rejects.toThrow();
545
+ });
546
+ ```
547
+
548
+ ## Quick Reference Checklist
549
+
550
+ When testing async code:
551
+
552
+ ```
553
+ □ Are all async functions awaited?
554
+ □ Are timeouts reasonable and not using real time?
555
+ □ Are both success and failure paths tested?
556
+ □ Are race conditions tested explicitly?
557
+ □ Are unhandled promise rejections caught?
558
+ □ Are async fixtures cleaned up properly?
559
+ □ Are concurrent operations tested?
560
+ □ Is error handling tested?
561
+ □ Are retry mechanisms tested?
562
+ □ Are loading/error states tested (for UI)?
563
+ ```
564
+
565
+ ## Remember
566
+
567
+ - **Async code is synchronous in tests** - Use await, don't rely on timing
568
+ - **Mock time** - Never use real delays in tests
569
+ - **Test failure paths** - Errors are part of async operations
570
+ - **Handle all promises** - Unhandled rejections cause flaky tests
571
+ - **Control concurrency** - Test race conditions deliberately
@@ -0,0 +1,143 @@
1
+ ---
2
+ skill_id: code-review
3
+ skill_version: 0.1.0
4
+ description: Systematic approach to reviewing code for quality, correctness, and maintainability.
5
+ updated_at: 2025-10-30T17:00:00Z
6
+ tags: [code-review, quality, collaboration, best-practices]
7
+ ---
8
+
9
+ # Code Review
10
+
11
+ Systematic approach to reviewing code for quality, correctness, and maintainability.
12
+
13
+ ## Code Review Checklist
14
+
15
+ ### Correctness
16
+ ```
17
+ □ Logic is correct and handles edge cases
18
+ □ Error handling is appropriate
19
+ □ No obvious bugs or issues
20
+ □ Test coverage is adequate
21
+ □ Code works as intended
22
+ ```
23
+
24
+ ### Design & Architecture
25
+ ```
26
+ □ Follows SOLID principles
27
+ □ Appropriate design patterns used
28
+ □ No unnecessary complexity
29
+ □ Good separation of concerns
30
+ □ Consistent with existing codebase
31
+ ```
32
+
33
+ ### Readability
34
+ ```
35
+ □ Clear variable and function names
36
+ □ Functions are small and focused
37
+ □ Comments explain "why" not "what"
38
+ □ Code is self-documenting
39
+ □ Consistent formatting
40
+ ```
41
+
42
+ ### Performance
43
+ ```
44
+ □ No obvious performance issues
45
+ □ Efficient algorithms used
46
+ □ Appropriate data structures
47
+ □ Database queries optimized
48
+ □ No memory leaks
49
+ ```
50
+
51
+ ### Security
52
+ ```
53
+ □ Input validation present
54
+ □ No SQL injection vulnerabilities
55
+ □ Authentication/authorization checks
56
+ □ No sensitive data exposed
57
+ □ Dependencies are up to date
58
+ ```
59
+
60
+ ### Tests
61
+ ```
62
+ □ New code has tests
63
+ □ Tests are meaningful
64
+ □ Edge cases tested
65
+ □ Tests follow AAA pattern
66
+ □ No flaky tests
67
+ ```
68
+
69
+ ## Review Comments
70
+
71
+ ### Be Constructive
72
+ ```
73
+ ❌ "This code is terrible"
74
+ ✅ "Consider extracting this into a separate function for clarity"
75
+
76
+ ❌ "Wrong approach"
77
+ ✅ "Have you considered using X pattern? It might simplify this"
78
+ ```
79
+
80
+ ### Be Specific
81
+ ```
82
+ ❌ "Improve this"
83
+ ✅ "This function is doing too much. Consider splitting into:
84
+ 1. Validation function
85
+ 2. Processing function
86
+ 3. Response builder"
87
+ ```
88
+
89
+ ### Provide Context
90
+ ```
91
+ ✅ "This could cause a race condition when multiple requests
92
+ access the cache simultaneously. Consider using a lock:
93
+
94
+ with cache_lock:
95
+ value = cache.get(key)
96
+ "
97
+ ```
98
+
99
+ ## Common Review Findings
100
+
101
+ ### Naming Issues
102
+ ```python
103
+ # Poor
104
+ def f(x):
105
+ return x * 2
106
+
107
+ # Better
108
+ def calculate_double(value):
109
+ return value * 2
110
+ ```
111
+
112
+ ### Error Handling
113
+ ```python
114
+ # Missing error handling
115
+ result = api.call()
116
+ process(result)
117
+
118
+ # With proper handling
119
+ try:
120
+ result = api.call()
121
+ process(result)
122
+ except APIError as e:
123
+ logger.error(f"API call failed: {e}")
124
+ return default_response()
125
+ ```
126
+
127
+ ### Magic Numbers
128
+ ```python
129
+ # Magic numbers
130
+ if user.age > 18 and user.balance > 1000:
131
+
132
+ # Named constants
133
+ MIN_AGE = 18
134
+ MIN_BALANCE = 1000
135
+ if user.age > MIN_AGE and user.balance > MIN_BALANCE:
136
+ ```
137
+
138
+ ## Remember
139
+ - Focus on code, not the person
140
+ - Explain the "why" behind suggestions
141
+ - Recognize good code too
142
+ - Suggest, don't demand
143
+ - Pick your battles - not every issue needs fixing