konductor 0.6.0 → 0.7.1

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,450 @@
1
+ # TDD Patterns — Test-Driven Development for Executors
2
+
3
+ This guide covers test-driven development (TDD) practices for executor agents implementing plans with `type = "tdd"`.
4
+
5
+ ## When to Use TDD
6
+
7
+ Use TDD when the plan frontmatter specifies `type = "tdd"`.
8
+
9
+ **TDD is appropriate for:**
10
+ - Pure functions with clear input/output contracts
11
+ - Business logic and validation rules
12
+ - Data transformations and parsing
13
+ - Algorithmic code (sorting, filtering, calculations)
14
+ - API endpoints with well-defined contracts
15
+
16
+ **TDD is NOT appropriate for:**
17
+ - UI components with complex interactions
18
+ - Infrastructure setup (databases, servers)
19
+ - Exploratory work with unclear requirements
20
+ - Simple CRUD operations with no business logic
21
+
22
+ ## The Red-Green-Refactor Cycle
23
+
24
+ TDD follows a three-phase cycle:
25
+
26
+ ### 1. Red Phase: Write Failing Tests
27
+
28
+ Write tests that express the desired behavior from the plan's `must_haves.truths`.
29
+
30
+ **Rules:**
31
+ - Tests should fail initially (no implementation exists yet)
32
+ - Tests should be specific and focused (one behavior per test)
33
+ - Test names should describe the behavior, not the implementation
34
+
35
+ **Example (Rust):**
36
+ ```rust
37
+ #[cfg(test)]
38
+ mod tests {
39
+ use super::*;
40
+
41
+ #[test]
42
+ fn test_password_validator_requires_minimum_length() {
43
+ let result = validate_password("short");
44
+ assert!(result.is_err());
45
+ assert_eq!(result.unwrap_err(), "Password must be at least 8 characters");
46
+ }
47
+
48
+ #[test]
49
+ fn test_password_validator_requires_uppercase() {
50
+ let result = validate_password("nouppercase123!");
51
+ assert!(result.is_err());
52
+ assert_eq!(result.unwrap_err(), "Password must contain at least one uppercase letter");
53
+ }
54
+
55
+ #[test]
56
+ fn test_password_validator_accepts_valid_password() {
57
+ let result = validate_password("ValidPass123!");
58
+ assert!(result.is_ok());
59
+ }
60
+ }
61
+ ```
62
+
63
+ **Commit after red phase:**
64
+ ```
65
+ test(auth-plan-2): add password validation tests (red phase)
66
+
67
+ Tests verify:
68
+ - Minimum 8 characters required
69
+ - At least one uppercase letter required
70
+ - Valid passwords accepted
71
+ ```
72
+
73
+ ### 2. Green Phase: Write Minimal Implementation
74
+
75
+ Write the simplest code that makes the tests pass.
76
+
77
+ **Rules:**
78
+ - Focus on making tests pass, not on perfection
79
+ - Avoid premature optimization
80
+ - Don't add features not covered by tests
81
+
82
+ **Example (Rust):**
83
+ ```rust
84
+ pub fn validate_password(password: &str) -> Result<(), String> {
85
+ if password.len() < 8 {
86
+ return Err("Password must be at least 8 characters".to_string());
87
+ }
88
+
89
+ if !password.chars().any(|c| c.is_uppercase()) {
90
+ return Err("Password must contain at least one uppercase letter".to_string());
91
+ }
92
+
93
+ Ok(())
94
+ }
95
+ ```
96
+
97
+ **Verify:**
98
+ ```bash
99
+ cargo test password
100
+ ```
101
+
102
+ **Commit after green phase:**
103
+ ```
104
+ feat(auth-plan-2): implement password validation (green phase)
105
+
106
+ Validates:
107
+ - Minimum length of 8 characters
108
+ - At least one uppercase letter
109
+ ```
110
+
111
+ ### 3. Refactor Phase: Improve Code Quality
112
+
113
+ Improve the code structure while keeping tests green.
114
+
115
+ **Rules:**
116
+ - Tests must continue to pass
117
+ - Improve readability, not functionality
118
+ - Extract common patterns
119
+ - Remove duplication
120
+
121
+ **Example (Rust):**
122
+ ```rust
123
+ pub fn validate_password(password: &str) -> Result<(), String> {
124
+ let validators = [
125
+ (|p: &str| p.len() >= 8, "Password must be at least 8 characters"),
126
+ (|p: &str| p.chars().any(|c| c.is_uppercase()), "Password must contain at least one uppercase letter"),
127
+ ];
128
+
129
+ for (validator, error_msg) in validators {
130
+ if !validator(password) {
131
+ return Err(error_msg.to_string());
132
+ }
133
+ }
134
+
135
+ Ok(())
136
+ }
137
+ ```
138
+
139
+ **Commit after refactor phase:**
140
+ ```
141
+ refactor(auth-plan-2): extract password validators into array
142
+
143
+ No behavior change. Improves readability and makes adding new validators easier.
144
+ ```
145
+
146
+ ## Test Structure
147
+
148
+ ### Arrange-Act-Assert (AAA) Pattern
149
+
150
+ Structure tests in three parts:
151
+
152
+ ```rust
153
+ #[test]
154
+ fn test_calculate_discount_for_vip_customer() {
155
+ // Arrange: Set up test data
156
+ let customer = Customer::new("vip-123", CustomerTier::VIP);
157
+ let order = Order::new(100.0);
158
+
159
+ // Act: Execute the behavior
160
+ let discount = calculate_discount(&customer, &order);
161
+
162
+ // Assert: Verify the result
163
+ assert_eq!(discount, 20.0); // VIP gets 20% discount
164
+ }
165
+ ```
166
+
167
+ ### Test Naming Conventions
168
+
169
+ Use descriptive names that express intent:
170
+
171
+ **Good:**
172
+ - `test_validates_email_format`
173
+ - `test_rejects_duplicate_usernames`
174
+ - `test_calculates_total_with_tax`
175
+
176
+ **Bad:**
177
+ - `test1`
178
+ - `test_user`
179
+ - `test_works`
180
+
181
+ ### Test Data
182
+
183
+ Use meaningful test data that represents real scenarios:
184
+
185
+ **Good:**
186
+ ```javascript
187
+ test('parses ISO date strings', () => {
188
+ const result = parseDate('2024-03-19T10:30:00Z');
189
+ expect(result.year).toBe(2024);
190
+ expect(result.month).toBe(3);
191
+ expect(result.day).toBe(19);
192
+ });
193
+ ```
194
+
195
+ **Bad:**
196
+ ```javascript
197
+ test('parses dates', () => {
198
+ const result = parseDate('foo');
199
+ expect(result).toBeDefined();
200
+ });
201
+ ```
202
+
203
+ ## Test-First for Observable Behaviors
204
+
205
+ Write tests for the plan's `must_haves.truths` before implementing.
206
+
207
+ **Example plan truth:** "Users cannot register with duplicate email addresses"
208
+
209
+ **Test first:**
210
+ ```python
211
+ def test_registration_rejects_duplicate_email():
212
+ # Arrange: Create first user
213
+ user1 = User.create(email="test@example.com", password="Pass123!")
214
+
215
+ # Act: Try to create second user with same email
216
+ with pytest.raises(DuplicateEmailError):
217
+ user2 = User.create(email="test@example.com", password="Pass456!")
218
+
219
+ # Assert: Only one user exists
220
+ assert User.count_by_email("test@example.com") == 1
221
+ ```
222
+
223
+ Then implement the logic to make it pass.
224
+
225
+ ## Verification Commands
226
+
227
+ Tests must be automated and run quickly (< 60 seconds).
228
+
229
+ **By language/framework:**
230
+
231
+ ### Rust (cargo test)
232
+ ```bash
233
+ cargo test # Run all tests
234
+ cargo test user:: # Run tests in user module
235
+ cargo test --lib # Run library tests only
236
+ cargo test -- --nocapture # Show println! output
237
+ ```
238
+
239
+ ### JavaScript/TypeScript (Jest/Vitest)
240
+ ```bash
241
+ npm test # Run all tests
242
+ npm test user.test.ts # Run specific test file
243
+ npm test -- --watch # Run in watch mode
244
+ npm test -- --coverage # Generate coverage report
245
+ ```
246
+
247
+ ### Python (pytest)
248
+ ```bash
249
+ pytest # Run all tests
250
+ pytest tests/test_user.py # Run specific test file
251
+ pytest -k "email" # Run tests matching "email"
252
+ pytest -v # Verbose output
253
+ ```
254
+
255
+ ### Go (go test)
256
+ ```bash
257
+ go test ./... # Run all tests in all packages
258
+ go test -v # Verbose output
259
+ go test -run TestUser # Run specific test
260
+ go test -cover # Show coverage
261
+ ```
262
+
263
+ ## Test Patterns by Stack
264
+
265
+ ### REST API Testing
266
+
267
+ ```javascript
268
+ // Jest + Supertest
269
+ test('POST /auth/register returns 201 with user JSON', async () => {
270
+ const response = await request(app)
271
+ .post('/auth/register')
272
+ .send({ email: 'test@example.com', password: 'Pass123!' })
273
+ .expect(201);
274
+
275
+ expect(response.body).toHaveProperty('id');
276
+ expect(response.body.email).toBe('test@example.com');
277
+ expect(response.body).not.toHaveProperty('password');
278
+ });
279
+ ```
280
+
281
+ ### Database Testing
282
+
283
+ ```python
284
+ # pytest with fixtures
285
+ @pytest.fixture
286
+ def db():
287
+ """Create a fresh database for each test"""
288
+ connection = create_test_db()
289
+ yield connection
290
+ connection.drop_all()
291
+
292
+ def test_user_saved_to_database(db):
293
+ user = User(email="test@example.com", password_hash="hashed")
294
+ db.save(user)
295
+
296
+ retrieved = db.query(User).filter_by(email="test@example.com").first()
297
+ assert retrieved is not None
298
+ assert retrieved.email == "test@example.com"
299
+ ```
300
+
301
+ ### Business Logic Testing
302
+
303
+ ```rust
304
+ #[test]
305
+ fn test_discount_calculation_for_bulk_orders() {
306
+ let order = Order {
307
+ items: vec![
308
+ Item { price: 10.0, quantity: 100 },
309
+ Item { price: 5.0, quantity: 200 },
310
+ ],
311
+ };
312
+
313
+ let discount = calculate_bulk_discount(&order);
314
+
315
+ assert_eq!(discount, 0.15); // 15% discount for orders > $1000
316
+ }
317
+ ```
318
+
319
+ ## Anti-Patterns to Avoid
320
+
321
+ ### 1. Testing Implementation Details
322
+
323
+ **Bad:**
324
+ ```javascript
325
+ test('uses Array.map internally', () => {
326
+ const spy = jest.spyOn(Array.prototype, 'map');
327
+ transform([1, 2, 3]);
328
+ expect(spy).toHaveBeenCalled();
329
+ });
330
+ ```
331
+
332
+ **Good:**
333
+ ```javascript
334
+ test('transforms array elements', () => {
335
+ const result = transform([1, 2, 3]);
336
+ expect(result).toEqual([2, 4, 6]);
337
+ });
338
+ ```
339
+
340
+ ### 2. Brittle Mocks
341
+
342
+ **Bad:**
343
+ ```python
344
+ def test_sends_welcome_email():
345
+ mock_mailer = Mock()
346
+ mock_mailer.send.return_value = True
347
+ mock_mailer.template_id = "welcome-123"
348
+ mock_mailer.from_address = "noreply@example.com"
349
+ # ... 10 more mock configurations
350
+
351
+ user.send_welcome_email(mock_mailer)
352
+ assert mock_mailer.send.called
353
+ ```
354
+
355
+ **Good:**
356
+ ```python
357
+ def test_sends_welcome_email():
358
+ mock_mailer = Mock()
359
+ user.send_welcome_email(mock_mailer)
360
+
361
+ # Verify behavior, not every internal detail
362
+ mock_mailer.send.assert_called_once()
363
+ args = mock_mailer.send.call_args[0]
364
+ assert "Welcome" in args[0] # Email subject
365
+ assert user.email in args[1] # Recipient
366
+ ```
367
+
368
+ ### 3. Testing Getters and Setters
369
+
370
+ **Bad:**
371
+ ```java
372
+ @Test
373
+ public void testSetName() {
374
+ User user = new User();
375
+ user.setName("John");
376
+ assertEquals("John", user.getName());
377
+ }
378
+ ```
379
+
380
+ **Why bad:** This tests language features, not your logic. Skip trivial getters/setters.
381
+
382
+ ### 4. One Giant Test
383
+
384
+ **Bad:**
385
+ ```javascript
386
+ test('user registration system', () => {
387
+ // Tests 10 different things in one test
388
+ // If it fails, you don't know which part broke
389
+ });
390
+ ```
391
+
392
+ **Good:**
393
+ ```javascript
394
+ test('validates email format during registration', () => { /* ... */ });
395
+ test('hashes password during registration', () => { /* ... */ });
396
+ test('creates user record in database', () => { /* ... */ });
397
+ test('sends welcome email after registration', () => { /* ... */ });
398
+ ```
399
+
400
+ ## Coverage Goals
401
+
402
+ Aim for high coverage of critical paths, not 100% coverage of all code.
403
+
404
+ **Prioritize testing:**
405
+ - Business logic and validation rules
406
+ - Error handling and edge cases
407
+ - Security-critical code (auth, permissions)
408
+ - Data transformations with complex rules
409
+
410
+ **Lower priority:**
411
+ - Simple CRUD operations
412
+ - Framework glue code
413
+ - Configuration files
414
+ - Trivial getters/setters
415
+
416
+ ## Summary Writing for TDD Plans
417
+
418
+ When writing your plan summary, include:
419
+
420
+ **Test Results:**
421
+ Paste the full output from your test runner showing all tests passing.
422
+
423
+ **Coverage:**
424
+ If the plan specifies coverage requirements, include coverage report.
425
+
426
+ **Red-Green-Refactor commits:**
427
+ List the commits you made for each phase of the TDD cycle.
428
+
429
+ **Example:**
430
+ ```markdown
431
+ ## Test Results
432
+ ```
433
+ npm test
434
+
435
+ PASS src/validation/password.test.ts
436
+ ✓ validates minimum length (3 ms)
437
+ ✓ requires uppercase letter (1 ms)
438
+ ✓ requires number (1 ms)
439
+ ✓ requires special character (2 ms)
440
+ ✓ accepts valid password (1 ms)
441
+
442
+ Test Suites: 1 passed, 1 total
443
+ Tests: 5 passed, 5 total
444
+ ```
445
+
446
+ ## Commits
447
+ 1. test(auth-plan-2): add password validation tests (red phase)
448
+ 2. feat(auth-plan-2): implement password validation (green phase)
449
+ 3. refactor(auth-plan-2): extract validators into reusable functions
450
+ ```
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: konductor-init
3
+ description: Initialize a new Konductor project with spec-driven development. Use when the user says init, initialize, new project, start project, set up konductor, or bootstrap.
4
+ ---
5
+
6
+ # Konductor Init — Project Initialization
7
+
8
+ You are the Konductor orchestrator. Initialize a new spec-driven development project.
9
+
10
+ ## Step 1: Check Existing State
11
+
12
+ Check if `.konductor/` directory already exists.
13
+ - If it exists, call the `state_get` MCP tool. If it returns valid state, warn the user: "A Konductor project already exists here. Reinitializing will overwrite project.md, requirements.md, and roadmap.md. Proceed? (y/n)"
14
+ - If user declines, stop.
15
+
16
+ ## Step 2: Create Directory Structure
17
+
18
+ Run:
19
+ ```bash
20
+ mkdir -p .konductor/phases .konductor/milestones .konductor/.results .konductor/.tracking
21
+ ```
22
+
23
+ ## Step 3: Detect Existing Codebase
24
+
25
+ Check for existing code files:
26
+ ```bash
27
+ ls src/ lib/ app/ package.json Cargo.toml go.mod pyproject.toml requirements.txt 2>/dev/null
28
+ ```
29
+
30
+ If any exist, this is a brownfield project. Use the **konductor-codebase-mapper** agent to analyze the codebase. Provide it with:
31
+ - The project root directory
32
+ - Instructions to map: file structure, tech stack, patterns, conventions, testing, integrations
33
+ - Output to: `.kiro/steering/structure.md` and `.kiro/steering/tech.md`
34
+
35
+ Wait for completion before proceeding.
36
+
37
+ ## Step 4: Project Discovery
38
+
39
+ Use the **konductor-discoverer** agent to interview the user. Provide it with:
40
+ - Instructions to ask 5-8 questions covering: project vision/purpose, target users, tech stack preferences, key features for v1, constraints/timeline, what's explicitly out of scope
41
+ - For brownfield projects: include the codebase analysis from Step 3 as context
42
+ - Reference: see `references/questioning.md` for question design guidelines
43
+ - Output: write `.konductor/project.md`, `.konductor/requirements.md`, `.konductor/roadmap.md`
44
+
45
+ Wait for the discoverer to complete.
46
+
47
+ ## Step 5: Verify Outputs
48
+
49
+ Confirm these files were created and are non-empty:
50
+ - `.konductor/project.md`
51
+ - `.konductor/requirements.md`
52
+ - `.konductor/roadmap.md`
53
+
54
+ If any are missing, report the error and stop.
55
+
56
+ ## Step 6: Write Initial State
57
+
58
+ Call the `state_init` MCP tool with:
59
+ - `name`: the project name from project.md
60
+ - `phase`: the first phase identifier from the roadmap
61
+ - `phases_total`: the total number of phases from the roadmap
62
+
63
+ This creates `.konductor/state.toml` with the correct initial structure.
64
+
65
+ Write `.konductor/config.toml`:
66
+ ```toml
67
+ [general]
68
+ default_model = "claude-sonnet-4"
69
+
70
+ [execution]
71
+ max_wave_parallelism = 4
72
+
73
+ [git]
74
+ auto_commit = true
75
+ branching_strategy = "none"
76
+
77
+ [features]
78
+ research = true
79
+ plan_checker = true
80
+ verifier = true
81
+ ```
82
+
83
+ ## Step 7: Sync Steering Files
84
+
85
+ Create/update `.kiro/steering/` files:
86
+ - `.kiro/steering/product.md` — extract purpose, target users, key features from project.md
87
+ - `.kiro/steering/tech.md` — extract tech stack, libraries, constraints from project.md (merge with codebase mapper output if brownfield)
88
+ - `.kiro/steering/structure.md` — if brownfield, already created by mapper. If greenfield, write recommended structure based on tech stack.
89
+
90
+ ## Step 8: Report Success
91
+
92
+ Tell the user:
93
+ - How many phases were identified in the roadmap
94
+ - List the phase names
95
+ - Suggest: "Say 'next' to start planning phase 1, or 'discuss phase 01' to set preferences first."