@champpaba/claude-agent-kit 1.1.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,891 @@
1
+ # TDD Workflow (Red-Green-Refactor)
2
+
3
+ > **Test-Driven Development methodology for complex/critical tasks**
4
+ > **Version:** 1.4.0
5
+ > **Purpose:** Eliminate duplication across backend/frontend/database agents
6
+
7
+ ---
8
+
9
+ ## 📋 Overview
10
+
11
+ This document defines the TDD (Test-Driven Development) workflow using the Red-Green-Refactor cycle.
12
+
13
+ **Why this exists:**
14
+ - ✅ Single source of truth for TDD methodology
15
+ - ✅ Prevents errors in critical code (auth, payment, migrations)
16
+ - ✅ Ensures testability (write tests first)
17
+ - ✅ Consistent TDD practice across agents
18
+
19
+ **Who uses this:**
20
+ - **backend agent:** When `tdd_required: true` (auth, payment, security)
21
+ - **frontend agent:** When `tdd_required: true` (complex state, multi-step forms)
22
+ - **database agent:** When `tdd_required: true` (migrations, schema changes)
23
+
24
+ **When to use TDD:**
25
+ - ✅ Authentication/authorization logic
26
+ - ✅ Payment processing
27
+ - ✅ Security-critical features
28
+ - ✅ Complex business logic
29
+ - ✅ Database migrations
30
+ - ✅ Multi-step workflows
31
+ - ✅ Any task with `tdd_required: true` flag
32
+
33
+ **When NOT to use TDD (use Test-Alongside):**
34
+ - ❌ Simple CRUD endpoints
35
+ - ❌ Read-only queries
36
+ - ❌ Presentational UI components
37
+ - ❌ Static pages
38
+ - ❌ Any task with `tdd_required: false` flag
39
+
40
+ ---
41
+
42
+ ## 🔄 The Red-Green-Refactor Cycle
43
+
44
+ ### Overview:
45
+
46
+ ```
47
+ 1. 🔴 RED Phase: Write Test First
48
+ ├─→ Test MUST fail (feature doesn't exist yet)
49
+ └─→ Defines expected behavior
50
+
51
+ 2. 🟢 GREEN Phase: Minimal Implementation
52
+ ├─→ Write just enough code to pass tests
53
+ └─→ Focus on making it work (not perfect)
54
+
55
+ 3. 🔵 REFACTOR Phase: Improve Code Quality
56
+ ├─→ Add error handling, logging, validation
57
+ ├─→ Improve structure and readability
58
+ └─→ Tests MUST still pass
59
+ ```
60
+
61
+ **Key Principle:** Tests drive the implementation, not the other way around.
62
+
63
+ ---
64
+
65
+ ## 🔴 RED Phase: Write Test First
66
+
67
+ ### Purpose:
68
+
69
+ - Define expected behavior BEFORE writing code
70
+ - Ensure feature is testable from the start
71
+ - Document requirements as executable tests
72
+
73
+ ### Protocol:
74
+
75
+ **Step 1: Write failing tests**
76
+
77
+ ```
78
+ ✅ DO:
79
+ - Write comprehensive test cases
80
+ - Cover success cases
81
+ - Cover error cases
82
+ - Cover edge cases
83
+ - Use descriptive test names
84
+
85
+ ❌ DON'T:
86
+ - Write implementation code yet
87
+ - Skip error cases
88
+ - Use vague test names
89
+ - Assume implementation details
90
+ ```
91
+
92
+ **Step 2: Run tests**
93
+
94
+ ```bash
95
+ # Expected result: ❌ FAILED
96
+ # This is CORRECT! Tests should fail in RED phase.
97
+ ```
98
+
99
+ **Step 3: Verify test failure**
100
+
101
+ ```
102
+ ✅ Test failed for the right reason:
103
+ - Feature doesn't exist (404, Connection refused)
104
+ - Endpoint not found
105
+ - Function not defined
106
+
107
+ ❌ Test failed for wrong reason:
108
+ - Syntax error in test
109
+ - Wrong import
110
+ - Test configuration issue
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 🟢 GREEN Phase: Minimal Implementation
116
+
117
+ ### Purpose:
118
+
119
+ - Make tests pass with simplest possible code
120
+ - Don't worry about perfect code yet
121
+ - Focus on correctness, not elegance
122
+
123
+ ### Protocol:
124
+
125
+ **Step 1: Write minimal code**
126
+
127
+ ```
128
+ ✅ DO:
129
+ - Write just enough to make tests pass
130
+ - Use hardcoded values (will refactor later)
131
+ - Keep it simple and straightforward
132
+
133
+ ❌ DON'T:
134
+ - Over-engineer
135
+ - Add unnecessary features
136
+ - Optimize prematurely
137
+ - Add complex error handling (save for REFACTOR)
138
+ ```
139
+
140
+ **Step 2: Run tests**
141
+
142
+ ```bash
143
+ # Expected result: ✅ PASSED
144
+ # All (or most) tests should pass now.
145
+ ```
146
+
147
+ **Step 3: Verify all tests pass**
148
+
149
+ ```
150
+ ✅ If all tests pass:
151
+ → Move to REFACTOR phase
152
+
153
+ ⚠️ If some tests still fail:
154
+ → Fix implementation (stay in GREEN phase)
155
+
156
+ ❌ If tests pass but you added extra features:
157
+ → Remove extra features (YAGNI principle)
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 🔵 REFACTOR Phase: Production Quality
163
+
164
+ ### Purpose:
165
+
166
+ - Improve code quality while keeping tests green
167
+ - Add error handling, logging, validation
168
+ - Optimize and clean up
169
+
170
+ ### Protocol:
171
+
172
+ **Step 1: Improve code quality**
173
+
174
+ ```
175
+ ✅ DO:
176
+ - Add error handling (try/catch, validation)
177
+ - Add structured logging (entry, success, error)
178
+ - Add type hints / TypeScript types
179
+ - Add docstrings / JSDoc comments
180
+ - Extract reusable functions
181
+ - Follow coding standards
182
+ - Improve variable names
183
+
184
+ ❌ DON'T:
185
+ - Change test expectations (tests are requirements)
186
+ - Break existing functionality
187
+ - Add untested features
188
+ ```
189
+
190
+ **Step 2: Run tests after EACH change**
191
+
192
+ ```bash
193
+ # After every refactor:
194
+ # Expected result: ✅ PASSED (tests still pass!)
195
+ ```
196
+
197
+ **Step 3: Verify tests still pass**
198
+
199
+ ```
200
+ ✅ If tests pass:
201
+ → Continue refactoring
202
+ → When satisfied, mark task complete
203
+
204
+ ❌ If tests fail:
205
+ → Undo last change
206
+ → Fix refactoring
207
+ → Run tests again
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 📚 Language-Specific Examples
213
+
214
+ ### Python (FastAPI + Pytest)
215
+
216
+ #### 🔴 RED Phase:
217
+
218
+ ```python
219
+ # tests/test_auth.py (WRITE THIS FIRST!)
220
+ import pytest
221
+ from httpx import AsyncClient
222
+
223
+ @pytest.mark.asyncio
224
+ async def test_login_success(client: AsyncClient):
225
+ """Test successful login with valid credentials"""
226
+ response = await client.post("/api/auth/login", json={
227
+ "email": "test@example.com",
228
+ "password": "password123"
229
+ })
230
+
231
+ assert response.status_code == 200
232
+ data = response.json()
233
+ assert "token" in data
234
+ assert data["user"]["email"] == "test@example.com"
235
+
236
+ @pytest.mark.asyncio
237
+ async def test_login_invalid_credentials(client: AsyncClient):
238
+ """Test invalid credentials return 401"""
239
+ response = await client.post("/api/auth/login", json={
240
+ "email": "wrong@example.com",
241
+ "password": "wrongpass"
242
+ })
243
+
244
+ assert response.status_code == 401
245
+ assert "invalid" in response.json()["detail"].lower()
246
+
247
+ @pytest.mark.asyncio
248
+ async def test_login_validation_error(client: AsyncClient):
249
+ """Test validation on missing fields"""
250
+ response = await client.post("/api/auth/login", json={
251
+ "email": "not-an-email"
252
+ # Missing password
253
+ })
254
+
255
+ assert response.status_code == 422 # Validation error
256
+ ```
257
+
258
+ **Run tests:**
259
+ ```bash
260
+ pytest tests/test_auth.py -v
261
+
262
+ # Expected: ❌ FAILED (endpoint doesn't exist)
263
+ ```
264
+
265
+ #### 🟢 GREEN Phase:
266
+
267
+ ```python
268
+ # app/api/auth.py (NOW write implementation)
269
+ from fastapi import APIRouter, HTTPException
270
+
271
+ router = APIRouter()
272
+
273
+ @router.post("/api/auth/login")
274
+ async def login(email: str, password: str):
275
+ """Minimal implementation - just make tests pass"""
276
+
277
+ # Hardcoded for now (will refactor later)
278
+ if email == "test@example.com" and password == "password123":
279
+ return {
280
+ "token": "fake-token-123",
281
+ "user": {"email": email}
282
+ }
283
+
284
+ # Invalid credentials
285
+ raise HTTPException(status_code=401, detail="Invalid credentials")
286
+ ```
287
+
288
+ **Run tests:**
289
+ ```bash
290
+ pytest tests/test_auth.py -v
291
+
292
+ # Expected: ✅ PASSED (2-3 tests passing)
293
+ ```
294
+
295
+ #### 🔵 REFACTOR Phase:
296
+
297
+ ```python
298
+ # app/api/auth.py (Refactor with production quality)
299
+ from fastapi import APIRouter, HTTPException, Depends
300
+ from pydantic import BaseModel, EmailStr
301
+ from sqlalchemy import select
302
+ from sqlalchemy.ext.asyncio import AsyncSession
303
+ from app.lib.logger import logger
304
+ from app.lib.auth import verify_password, create_jwt_token
305
+ from app.db import get_db
306
+ from app.models.user import User
307
+
308
+ router = APIRouter()
309
+
310
+ class LoginRequest(BaseModel):
311
+ """Login request validation schema"""
312
+ email: EmailStr
313
+ password: str
314
+
315
+ class LoginResponse(BaseModel):
316
+ """Login response schema"""
317
+ token: str
318
+ user: dict
319
+
320
+ @router.post("/api/auth/login", response_model=LoginResponse)
321
+ async def login(
322
+ data: LoginRequest,
323
+ db: AsyncSession = Depends(get_db)
324
+ ):
325
+ """
326
+ Authenticate user and return JWT token.
327
+
328
+ Raises:
329
+ HTTPException 401: Invalid credentials
330
+ HTTPException 500: Server error
331
+ """
332
+
333
+ # Log entry
334
+ logger.info("api_route_entry", extra={
335
+ "route": "/api/auth/login",
336
+ "method": "POST",
337
+ "email": data.email
338
+ })
339
+
340
+ try:
341
+ # Query database
342
+ result = await db.execute(
343
+ select(User).where(User.email == data.email)
344
+ )
345
+ user = result.scalar_one_or_none()
346
+
347
+ # Verify credentials
348
+ if not user or not verify_password(data.password, user.hashed_password):
349
+ logger.warning("login_failed", extra={
350
+ "email": data.email,
351
+ "reason": "invalid_credentials"
352
+ })
353
+ raise HTTPException(
354
+ status_code=401,
355
+ detail="Invalid credentials"
356
+ )
357
+
358
+ # Generate JWT token
359
+ token = create_jwt_token(user.id)
360
+
361
+ # Log success
362
+ logger.info("login_success", extra={
363
+ "user_id": user.id,
364
+ "email": data.email
365
+ })
366
+
367
+ return LoginResponse(
368
+ token=token,
369
+ user={
370
+ "id": user.id,
371
+ "email": user.email,
372
+ "name": user.name
373
+ }
374
+ )
375
+
376
+ except HTTPException:
377
+ raise
378
+ except Exception as e:
379
+ logger.error("login_error", extra={
380
+ "error": str(e),
381
+ "email": data.email
382
+ })
383
+ raise HTTPException(
384
+ status_code=500,
385
+ detail="Internal server error"
386
+ )
387
+ ```
388
+
389
+ **Run tests:**
390
+ ```bash
391
+ pytest tests/test_auth.py -v
392
+
393
+ # Expected: ✅ PASSED (all tests still passing!)
394
+ ```
395
+
396
+ ---
397
+
398
+ ### TypeScript (Next.js + Vitest)
399
+
400
+ #### 🔴 RED Phase:
401
+
402
+ ```typescript
403
+ // __tests__/api/auth.test.ts (WRITE THIS FIRST!)
404
+ import { describe, it, expect } from 'vitest'
405
+ import { POST } from '@/app/api/auth/login/route'
406
+
407
+ describe('POST /api/auth/login', () => {
408
+ it('should return 200 and token for valid credentials', async () => {
409
+ const request = new Request('http://localhost/api/auth/login', {
410
+ method: 'POST',
411
+ body: JSON.stringify({
412
+ email: 'test@example.com',
413
+ password: 'password123'
414
+ })
415
+ })
416
+
417
+ const response = await POST(request)
418
+ const data = await response.json()
419
+
420
+ expect(response.status).toBe(200)
421
+ expect(data).toHaveProperty('token')
422
+ expect(data.user.email).toBe('test@example.com')
423
+ })
424
+
425
+ it('should return 401 for invalid credentials', async () => {
426
+ const request = new Request('http://localhost/api/auth/login', {
427
+ method: 'POST',
428
+ body: JSON.stringify({
429
+ email: 'wrong@example.com',
430
+ password: 'wrongpass'
431
+ })
432
+ })
433
+
434
+ const response = await POST(request)
435
+ const data = await response.json()
436
+
437
+ expect(response.status).toBe(401)
438
+ expect(data.error).toMatch(/invalid/i)
439
+ })
440
+
441
+ it('should return 422 for missing email', async () => {
442
+ const request = new Request('http://localhost/api/auth/login', {
443
+ method: 'POST',
444
+ body: JSON.stringify({
445
+ password: 'password123'
446
+ // Missing email
447
+ })
448
+ })
449
+
450
+ const response = await POST(request)
451
+
452
+ expect(response.status).toBe(422)
453
+ })
454
+ })
455
+ ```
456
+
457
+ **Run tests:**
458
+ ```bash
459
+ vitest run __tests__/api/auth.test.ts
460
+
461
+ # Expected: ❌ FAILED (route doesn't exist)
462
+ ```
463
+
464
+ #### 🟢 GREEN Phase:
465
+
466
+ ```typescript
467
+ // app/api/auth/login/route.ts (NOW write implementation)
468
+ import { NextResponse } from 'next/server'
469
+
470
+ export async function POST(request: Request) {
471
+ const body = await request.json()
472
+ const { email, password } = body
473
+
474
+ // Hardcoded for now (will refactor later)
475
+ if (email === 'test@example.com' && password === 'password123') {
476
+ return NextResponse.json({
477
+ token: 'fake-token-123',
478
+ user: { email }
479
+ })
480
+ }
481
+
482
+ // Invalid credentials
483
+ return NextResponse.json(
484
+ { error: 'Invalid credentials' },
485
+ { status: 401 }
486
+ )
487
+ }
488
+ ```
489
+
490
+ **Run tests:**
491
+ ```bash
492
+ vitest run __tests__/api/auth.test.ts
493
+
494
+ # Expected: ✅ PASSED (2-3 tests passing)
495
+ ```
496
+
497
+ #### 🔵 REFACTOR Phase:
498
+
499
+ ```typescript
500
+ // app/api/auth/login/route.ts (Refactor with production quality)
501
+ import { NextResponse } from 'next/server'
502
+ import { z } from 'zod'
503
+ import { logger } from '@/lib/logger'
504
+ import { verifyPassword, createJwtToken } from '@/lib/auth'
505
+ import { prisma } from '@/lib/db'
506
+
507
+ const LoginSchema = z.object({
508
+ email: z.string().email('Invalid email format'),
509
+ password: z.string().min(8, 'Password must be at least 8 characters')
510
+ })
511
+
512
+ export async function POST(request: Request) {
513
+ // Log entry
514
+ logger.info('api_route_entry', {
515
+ route: '/api/auth/login',
516
+ method: 'POST'
517
+ })
518
+
519
+ try {
520
+ // Parse and validate request
521
+ const body = await request.json()
522
+ const validation = LoginSchema.safeParse(body)
523
+
524
+ if (!validation.success) {
525
+ logger.warning('validation_error', {
526
+ errors: validation.error.errors
527
+ })
528
+ return NextResponse.json(
529
+ { error: 'Validation failed', details: validation.error.errors },
530
+ { status: 422 }
531
+ )
532
+ }
533
+
534
+ const { email, password } = validation.data
535
+
536
+ // Query database
537
+ const user = await prisma.user.findUnique({
538
+ where: { email }
539
+ })
540
+
541
+ // Verify credentials
542
+ if (!user || !(await verifyPassword(password, user.hashedPassword))) {
543
+ logger.warning('login_failed', {
544
+ email,
545
+ reason: 'invalid_credentials'
546
+ })
547
+ return NextResponse.json(
548
+ { error: 'Invalid credentials' },
549
+ { status: 401 }
550
+ )
551
+ }
552
+
553
+ // Generate JWT token
554
+ const token = createJwtToken(user.id)
555
+
556
+ // Log success
557
+ logger.info('login_success', {
558
+ userId: user.id,
559
+ email: user.email
560
+ })
561
+
562
+ return NextResponse.json({
563
+ token,
564
+ user: {
565
+ id: user.id,
566
+ email: user.email,
567
+ name: user.name
568
+ }
569
+ })
570
+
571
+ } catch (error) {
572
+ logger.error('login_error', {
573
+ error: error instanceof Error ? error.message : 'Unknown error'
574
+ })
575
+ return NextResponse.json(
576
+ { error: 'Internal server error' },
577
+ { status: 500 }
578
+ )
579
+ }
580
+ }
581
+ ```
582
+
583
+ **Run tests:**
584
+ ```bash
585
+ vitest run __tests__/api/auth.test.ts
586
+
587
+ # Expected: ✅ PASSED (all tests still passing!)
588
+ ```
589
+
590
+ ---
591
+
592
+ ### JavaScript (Express + Jest)
593
+
594
+ #### 🔴 RED Phase:
595
+
596
+ ```javascript
597
+ // __tests__/auth.test.js (WRITE THIS FIRST!)
598
+ const request = require('supertest')
599
+ const app = require('../app')
600
+
601
+ describe('POST /api/auth/login', () => {
602
+ it('should return 200 and token for valid credentials', async () => {
603
+ const response = await request(app)
604
+ .post('/api/auth/login')
605
+ .send({
606
+ email: 'test@example.com',
607
+ password: 'password123'
608
+ })
609
+
610
+ expect(response.status).toBe(200)
611
+ expect(response.body).toHaveProperty('token')
612
+ expect(response.body.user.email).toBe('test@example.com')
613
+ })
614
+
615
+ it('should return 401 for invalid credentials', async () => {
616
+ const response = await request(app)
617
+ .post('/api/auth/login')
618
+ .send({
619
+ email: 'wrong@example.com',
620
+ password: 'wrongpass'
621
+ })
622
+
623
+ expect(response.status).toBe(401)
624
+ expect(response.body.error).toMatch(/invalid/i)
625
+ })
626
+ })
627
+ ```
628
+
629
+ **Run tests:**
630
+ ```bash
631
+ npm test -- auth.test.js
632
+
633
+ # Expected: ❌ FAILED (route doesn't exist)
634
+ ```
635
+
636
+ #### 🟢 GREEN Phase:
637
+
638
+ ```javascript
639
+ // routes/auth.js (NOW write implementation)
640
+ const express = require('express')
641
+ const router = express.Router()
642
+
643
+ router.post('/api/auth/login', (req, res) => {
644
+ const { email, password } = req.body
645
+
646
+ // Hardcoded for now (will refactor later)
647
+ if (email === 'test@example.com' && password === 'password123') {
648
+ return res.json({
649
+ token: 'fake-token-123',
650
+ user: { email }
651
+ })
652
+ }
653
+
654
+ // Invalid credentials
655
+ return res.status(401).json({ error: 'Invalid credentials' })
656
+ })
657
+
658
+ module.exports = router
659
+ ```
660
+
661
+ **Run tests:**
662
+ ```bash
663
+ npm test -- auth.test.js
664
+
665
+ # Expected: ✅ PASSED
666
+ ```
667
+
668
+ #### 🔵 REFACTOR Phase:
669
+
670
+ ```javascript
671
+ // routes/auth.js (Refactor with production quality)
672
+ const express = require('express')
673
+ const { z } = require('zod')
674
+ const logger = require('../lib/logger')
675
+ const { verifyPassword, createJwtToken } = require('../lib/auth')
676
+ const prisma = require('../lib/db')
677
+
678
+ const router = express.Router()
679
+
680
+ const LoginSchema = z.object({
681
+ email: z.string().email(),
682
+ password: z.string().min(8)
683
+ })
684
+
685
+ router.post('/api/auth/login', async (req, res) => {
686
+ // Log entry
687
+ logger.info('api_route_entry', {
688
+ route: '/api/auth/login',
689
+ method: 'POST'
690
+ })
691
+
692
+ try {
693
+ // Validate request
694
+ const validation = LoginSchema.safeParse(req.body)
695
+ if (!validation.success) {
696
+ logger.warning('validation_error', { errors: validation.error.errors })
697
+ return res.status(422).json({
698
+ error: 'Validation failed',
699
+ details: validation.error.errors
700
+ })
701
+ }
702
+
703
+ const { email, password } = validation.data
704
+
705
+ // Query database
706
+ const user = await prisma.user.findUnique({ where: { email } })
707
+
708
+ // Verify credentials
709
+ if (!user || !(await verifyPassword(password, user.hashedPassword))) {
710
+ logger.warning('login_failed', { email, reason: 'invalid_credentials' })
711
+ return res.status(401).json({ error: 'Invalid credentials' })
712
+ }
713
+
714
+ // Generate JWT token
715
+ const token = createJwtToken(user.id)
716
+
717
+ // Log success
718
+ logger.info('login_success', { userId: user.id, email: user.email })
719
+
720
+ return res.json({
721
+ token,
722
+ user: {
723
+ id: user.id,
724
+ email: user.email,
725
+ name: user.name
726
+ }
727
+ })
728
+
729
+ } catch (error) {
730
+ logger.error('login_error', { error: error.message })
731
+ return res.status(500).json({ error: 'Internal server error' })
732
+ }
733
+ })
734
+
735
+ module.exports = router
736
+ ```
737
+
738
+ **Run tests:**
739
+ ```bash
740
+ npm test -- auth.test.js
741
+
742
+ # Expected: ✅ PASSED (all tests still passing!)
743
+ ```
744
+
745
+ ---
746
+
747
+ ## 🔀 TDD vs Test-Alongside
748
+
749
+ ### When to Use TDD (Red-Green-Refactor):
750
+
751
+ **Use when `tdd_required: true`:**
752
+
753
+ | Scenario | Why TDD? |
754
+ |----------|----------|
755
+ | Authentication/authorization | Security-critical, complex logic |
756
+ | Payment processing | Financial data, zero tolerance for bugs |
757
+ | Database migrations | Schema changes affect entire system |
758
+ | Multi-step workflows | Complex state transitions |
759
+ | Business logic (calculations) | Need to verify correctness |
760
+
761
+ ### When to Use Test-Alongside:
762
+
763
+ **Use when `tdd_required: false`:**
764
+
765
+ | Scenario | Why Test-Alongside? |
766
+ |----------|---------------------|
767
+ | Simple CRUD endpoints | Straightforward, low risk |
768
+ | Read-only queries | No side effects |
769
+ | Presentational UI | Visual, not logic-heavy |
770
+ | Static pages | No dynamic behavior |
771
+
772
+ ### Test-Alongside Workflow:
773
+
774
+ ```
775
+ 1. Write implementation first
776
+ 2. Write tests after
777
+ 3. Run tests to verify
778
+ 4. Refactor if needed
779
+ ```
780
+
781
+ **Example:**
782
+
783
+ ```python
784
+ # Step 1: Write implementation
785
+ @router.get("/api/users")
786
+ async def list_users(db: AsyncSession = Depends(get_db)):
787
+ result = await db.execute(select(User))
788
+ return {"users": [user.dict() for user in result.scalars().all()]}
789
+
790
+ # Step 2: Write tests
791
+ @pytest.mark.asyncio
792
+ async def test_list_users(client):
793
+ response = await client.get("/api/users")
794
+ assert response.status_code == 200
795
+ assert "users" in response.json()
796
+ ```
797
+
798
+ ---
799
+
800
+ ## 📊 Logging TDD Phases
801
+
802
+ ### RED Phase Log:
803
+
804
+ ```json
805
+ {
806
+ "event": "tdd_red_phase",
807
+ "task": "Implement POST /api/auth/login",
808
+ "test_file": "tests/test_auth.py",
809
+ "tests_written": 3,
810
+ "status": "fail",
811
+ "expected": "Tests should fail - endpoint not implemented yet"
812
+ }
813
+ ```
814
+
815
+ ### GREEN Phase Log:
816
+
817
+ ```json
818
+ {
819
+ "event": "tdd_green_phase",
820
+ "task": "Implement POST /api/auth/login",
821
+ "tests_passed": 2,
822
+ "tests_failed": 1,
823
+ "implementation": "app/api/auth.py",
824
+ "status": "partial_pass",
825
+ "note": "Minimal implementation complete, refactor needed"
826
+ }
827
+ ```
828
+
829
+ ### REFACTOR Phase Log:
830
+
831
+ ```json
832
+ {
833
+ "event": "tdd_refactor_phase",
834
+ "task": "Implement POST /api/auth/login",
835
+ "tests_passing": 3,
836
+ "improvements": [
837
+ "Added Pydantic validation schema",
838
+ "Added database integration",
839
+ "Added JWT token generation",
840
+ "Added structured logging",
841
+ "Added proper error handling"
842
+ ],
843
+ "status": "complete"
844
+ }
845
+ ```
846
+
847
+ ---
848
+
849
+ ## 🎯 Quick Reference
850
+
851
+ ### TDD Checklist:
852
+
853
+ ```markdown
854
+ Before starting TDD workflow:
855
+
856
+ [ ] Verify `tdd_required: true` flag
857
+ [ ] Choose appropriate testing framework (Pytest, Vitest, Jest)
858
+ [ ] Read existing test patterns (search for similar tests)
859
+
860
+ 🔴 RED Phase:
861
+ [ ] Write comprehensive test cases (success + errors + edge)
862
+ [ ] Run tests → verify they FAIL for right reason
863
+ [ ] Log RED phase completion
864
+
865
+ 🟢 GREEN Phase:
866
+ [ ] Write minimal implementation (hardcoded OK)
867
+ [ ] Run tests → verify they PASS
868
+ [ ] Log GREEN phase completion
869
+
870
+ 🔵 REFACTOR Phase:
871
+ [ ] Add error handling
872
+ [ ] Add logging (entry, success, error)
873
+ [ ] Add validation
874
+ [ ] Add type hints/comments
875
+ [ ] Run tests after EACH change → verify still PASS
876
+ [ ] Log REFACTOR phase completion
877
+
878
+ ✅ Final:
879
+ [ ] All tests passing
880
+ [ ] Code quality high (error handling, logging, types)
881
+ [ ] Ready for handoff
882
+ ```
883
+
884
+ ---
885
+
886
+ ## 🔗 See Also
887
+
888
+ - `tdd-classifier.md` - Logic to determine when `tdd_required: true`
889
+ - `validation-framework.md` - Agent validation requirements
890
+ - `context-loading-protocol.md` - How agents load testing frameworks
891
+ - `contexts/patterns/testing.md` - General testing patterns