jettypod 4.1.4 → 4.1.6

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.
@@ -5,6 +5,19 @@ description: Guide implementation of stable mode chores with comprehensive testi
5
5
 
6
6
  # Stable Mode Skill
7
7
 
8
+ ```
9
+ ┌─────────────────────────────────────────────────────────────────────┐
10
+ │ Mode Progression Flow │
11
+ │ │
12
+ │ Feature Planning → Speed Mode → [STABLE MODE] → Production Mode │
13
+ │ ▲▲▲▲▲▲▲▲▲▲▲▲▲ │
14
+ │ YOU ARE HERE │
15
+ │ │
16
+ │ Next: After stable mode implementation, feature can be marked │
17
+ │ complete, or elevated to production mode if needed. │
18
+ └─────────────────────────────────────────────────────────────────────┘
19
+ ```
20
+
8
21
  Guides Claude Code through stable mode implementation with comprehensive testing focus. Users confirm approach but Claude Code writes the code.
9
22
 
10
23
  ## Instructions
@@ -62,6 +75,23 @@ When this skill is activated, you are helping implement a stable mode chore to a
62
75
 
63
76
  ---
64
77
 
78
+ ## Quick Reference: Async Boundaries
79
+
80
+ **Where Claude Code MUST wait for user confirmation:**
81
+
82
+ | Phase | Location | Why |
83
+ |-------|----------|-----|
84
+ | Step 3 Phase 1 | Before implementing | User confirms implementation approach |
85
+
86
+ **Where Claude Code executes autonomously:**
87
+ - Step 0: Create additional scenarios (if first stable chore)
88
+ - Step 1: Scenario analysis
89
+ - Step 2: Speed mode implementation review
90
+ - Step 3 Phase 2: Autonomous execution loop
91
+ - Step 4: Completion check and routing
92
+
93
+ ---
94
+
65
95
  ## ⚠️ CRITICAL: Stable Mode Workflow Requirements for External Products
66
96
 
67
97
  **If the product is EXTERNAL (project_state: 'external'), DO NOT mark features as complete after stable mode.**
@@ -93,6 +123,11 @@ The validation will require you to either:
93
123
 
94
124
  ## Implementation Steps
95
125
 
126
+ <!-- ═══════════════════════════════════════════════════════════════════════════
127
+ PHASE 1: AUTONOMOUS SETUP
128
+ No user input required - Claude Code executes independently
129
+ ═══════════════════════════════════════════════════════════════════════════ -->
130
+
96
131
  ### Step 0: Create Additional Scenarios (If First Stable Chore)
97
132
 
98
133
  **CRITICAL:** If this is the FIRST stable mode chore for this feature, you must ADD edge case scenarios and step definitions.
@@ -144,43 +179,43 @@ Scenario: [Edge case title]
144
179
  **Code to get scenario (with error handling):**
145
180
 
146
181
  ```javascript
182
+ // --- Imports ---
147
183
  const { getCurrentWork } = require('../../lib/current-work');
148
184
  const { getDb } = require('../../lib/database');
149
185
  const fs = require('fs');
150
186
  const path = require('path');
151
187
 
152
- try {
153
- const currentWork = getCurrentWork();
154
-
155
- // Error handling: Check if current work exists
156
- if (!currentWork) {
157
- console.error('❌ No current work found. Run: jettypod work start <chore-id>');
158
- return;
159
- }
188
+ (async () => {
189
+ try {
190
+ const currentWork = await getCurrentWork();
160
191
 
161
- // Error handling: Check if parent exists
162
- if (!currentWork.parent_id) {
163
- console.error('❌ Current work has no parent feature. This chore must be part of a feature.');
164
- return;
165
- }
192
+ // --- Validate current work ---
193
+ if (!currentWork) {
194
+ console.error('❌ No current work found. Run: jettypod work start <chore-id>');
195
+ return;
196
+ }
166
197
 
167
- const db = getDb();
168
- db.get('SELECT * FROM work_items WHERE id = ?', [currentWork.parent_id], (err, feature) => {
169
- // Error handling: Database errors
170
- if (err) {
171
- console.error('❌ Database error:', err.message);
172
- db.close();
198
+ if (!currentWork.parent_id) {
199
+ console.error(' Current work has no parent feature. This chore must be part of a feature.');
173
200
  return;
174
201
  }
175
202
 
176
- // Error handling: Feature not found
203
+ // --- Get parent feature ---
204
+ const db = getDb();
205
+ const feature = await new Promise((resolve, reject) => {
206
+ db.get('SELECT * FROM work_items WHERE id = ?', [currentWork.parent_id], (err, row) => {
207
+ if (err) reject(err);
208
+ else resolve(row);
209
+ });
210
+ });
211
+
212
+ // --- Validate feature ---
177
213
  if (!feature) {
178
214
  console.error('❌ Parent feature not found in database.');
179
215
  db.close();
180
216
  return;
181
217
  }
182
218
 
183
- // Error handling: No scenario file
184
219
  if (!feature.scenario_file) {
185
220
  console.error('❌ Feature has no scenario_file. Cannot determine what to implement.');
186
221
  console.log('Suggestion: Create a scenario file and update the feature.');
@@ -188,9 +223,9 @@ try {
188
223
  return;
189
224
  }
190
225
 
226
+ // --- Validate scenario file exists ---
191
227
  const scenarioPath = path.join(process.cwd(), feature.scenario_file);
192
228
 
193
- // Error handling: Scenario file doesn't exist
194
229
  if (!fs.existsSync(scenarioPath)) {
195
230
  console.error(`❌ Scenario file not found: ${scenarioPath}`);
196
231
  console.log('Suggestion: Create the scenario file or update the feature.scenario_file path.');
@@ -198,7 +233,7 @@ try {
198
233
  return;
199
234
  }
200
235
 
201
- // Error handling: File read errors
236
+ // --- Read scenario content ---
202
237
  let scenarioContent;
203
238
  try {
204
239
  scenarioContent = fs.readFileSync(scenarioPath, 'utf8');
@@ -208,7 +243,7 @@ try {
208
243
  return;
209
244
  }
210
245
 
211
- // Error handling: Empty scenario file
246
+ // --- Validate content ---
212
247
  if (!scenarioContent || scenarioContent.trim().length === 0) {
213
248
  console.error('❌ Scenario file is empty.');
214
249
  db.close();
@@ -217,28 +252,27 @@ try {
217
252
 
218
253
  // Parse all scenarios...
219
254
  db.close();
220
- });
221
- } catch (err) {
222
- console.error('❌ Unexpected error in Step 1:', err.message);
223
- return;
224
- }
255
+ } catch (err) {
256
+ console.error('❌ Unexpected error in Step 1:', err.message);
257
+ return;
258
+ }
259
+ })();
225
260
  ```
226
261
 
227
262
  **Identify target scenario (with error handling):**
228
263
 
229
264
  ```javascript
230
- // Parse scenarios from Gherkin content
265
+ // --- Parse scenarios from Gherkin ---
231
266
  const scenarios = [];
232
267
  const scenarioBlocks = scenarioContent.split(/\nScenario:/);
233
268
 
234
- // Error handling: No scenarios found
235
269
  if (scenarioBlocks.length < 2) {
236
270
  console.error('❌ No scenarios found in scenario file.');
237
271
  console.log('Suggestion: Add Gherkin scenarios to the feature file.');
238
272
  return;
239
273
  }
240
274
 
241
- // Parse each scenario
275
+ // --- Extract scenario titles ---
242
276
  for (let i = 1; i < scenarioBlocks.length; i++) {
243
277
  const block = 'Scenario:' + scenarioBlocks[i];
244
278
  const titleMatch = block.match(/Scenario:\s*(.+)/);
@@ -246,11 +280,11 @@ for (let i = 1; i < scenarioBlocks.length; i++) {
246
280
  scenarios.push({ title, content: block });
247
281
  }
248
282
 
249
- // Match scenario to chore
283
+ // --- Match scenario to chore ---
250
284
  const choreDesc = currentWork.description.toLowerCase();
251
285
  let targetScenario = null;
252
286
 
253
- // Try to match by scenario number in chore description
287
+ // --- Try match by scenario number ---
254
288
  const scenarioNumMatch = choreDesc.match(/scenario\s+(\d+)/);
255
289
  if (scenarioNumMatch) {
256
290
  const num = parseInt(scenarioNumMatch[1]);
@@ -259,14 +293,12 @@ if (scenarioNumMatch) {
259
293
  }
260
294
  }
261
295
 
262
- // Try to match by keywords if no number match
296
+ // --- Try match by keywords ---
263
297
  if (!targetScenario) {
264
298
  for (const scenario of scenarios) {
265
299
  const scenarioLower = scenario.title.toLowerCase();
266
- // Skip happy path (usually first scenario)
267
- if (scenarios.indexOf(scenario) === 0) continue;
300
+ if (scenarios.indexOf(scenario) === 0) continue; // Skip happy path
268
301
 
269
- // Match keywords from chore description
270
302
  const keywords = choreDesc.split(/\s+/).filter(w => w.length > 3);
271
303
  const matches = keywords.filter(k => scenarioLower.includes(k));
272
304
 
@@ -277,7 +309,7 @@ if (!targetScenario) {
277
309
  }
278
310
  }
279
311
 
280
- // Error handling: No matching scenario
312
+ // --- Handle no match ---
281
313
  if (!targetScenario) {
282
314
  console.error('❌ Cannot match chore to any scenario in feature file.');
283
315
  console.log('Available scenarios:');
@@ -318,35 +350,28 @@ Now reviewing speed mode implementation...
318
350
  **Find speed mode files (with error handling):**
319
351
 
320
352
  ```javascript
353
+ // --- Imports and setup ---
321
354
  const { exec } = require('child_process');
322
355
  const util = require('util');
323
356
  const execPromise = util.promisify(exec);
324
357
 
325
- // Try to find files from git history
326
358
  let speedModeFiles = [];
327
359
 
328
360
  try {
329
- // Get commits for this feature
361
+ // --- Search git history for feature commits ---
330
362
  const featureName = feature.title.toLowerCase().replace(/\s+/g, '-');
331
363
  const { stdout: gitLog } = await execPromise(
332
364
  `git log --oneline --all --grep="${featureName}" -10`
333
365
  );
334
366
 
335
- // Error handling: No commits found
367
+ // --- Handle no commits found ---
336
368
  if (!gitLog || gitLog.trim().length === 0) {
337
369
  console.log('⚠️ No git commits found for this feature.');
338
- console.log('Asking user for files created in speed mode...');
339
-
340
- // Ask user which files were created
341
370
  console.log('\n📝 Which files did speed mode create/modify for this feature?');
342
- console.log('(List file paths, one per line, or type "none" if no files exist)\n');
343
-
344
- // Wait for user response...
345
- // If user says "none", handle gracefully
346
371
  return;
347
372
  }
348
373
 
349
- // Get files changed in those commits
374
+ // --- Extract files from commits ---
350
375
  const commits = gitLog.trim().split('\n').map(line => line.split(' ')[0]);
351
376
 
352
377
  for (const commit of commits) {
@@ -355,24 +380,16 @@ try {
355
380
  const fileList = files.trim().split('\n').filter(f => f.length > 0);
356
381
  speedModeFiles.push(...fileList);
357
382
  } catch (diffErr) {
358
- // Ignore errors for individual commits
359
383
  continue;
360
384
  }
361
385
  }
362
386
 
363
- // Remove duplicates
387
+ // --- Remove duplicates and validate ---
364
388
  speedModeFiles = [...new Set(speedModeFiles)];
365
389
 
366
- // Error handling: No files found
367
390
  if (speedModeFiles.length === 0) {
368
391
  console.error('❌ No files found in git history for this feature.');
369
- console.log('Suggestion: Either:');
370
- console.log(' 1. Specify files manually');
371
- console.log(' 2. Check if speed mode committed changes');
372
- console.log(' 3. Start fresh if speed mode was not completed\n');
373
-
374
- // Ask user for files
375
- console.log('📝 Which files should be reviewed? (or type "start-fresh" to begin from scratch)');
392
+ console.log('📝 Which files should be reviewed?');
376
393
  return;
377
394
  }
378
395
 
@@ -380,26 +397,22 @@ try {
380
397
 
381
398
  } catch (gitErr) {
382
399
  console.error('⚠️ Git error:', gitErr.message);
383
- console.log('Falling back to manual file specification...\n');
384
-
385
400
  console.log('📝 Which files did speed mode create/modify?');
386
401
  return;
387
402
  }
388
403
 
389
- // Validate files are readable
404
+ // --- Validate files are readable ---
390
405
  const readableFiles = [];
391
406
  const unreadableFiles = [];
392
407
 
393
408
  for (const filePath of speedModeFiles) {
394
409
  const fullPath = path.join(process.cwd(), filePath);
395
410
 
396
- // Error handling: File doesn't exist
397
411
  if (!fs.existsSync(fullPath)) {
398
412
  console.log(`⚠️ File no longer exists: ${filePath}`);
399
413
  continue;
400
414
  }
401
415
 
402
- // Error handling: File not readable
403
416
  try {
404
417
  fs.accessSync(fullPath, fs.constants.R_OK);
405
418
  readableFiles.push(filePath);
@@ -409,16 +422,15 @@ for (const filePath of speedModeFiles) {
409
422
  }
410
423
  }
411
424
 
412
- // Error handling: No readable files
425
+ // --- Final validation ---
413
426
  if (readableFiles.length === 0) {
414
427
  console.error('❌ No readable speed mode files found.');
415
- console.log('Cannot proceed without existing implementation to review.');
416
- console.log('\nSuggestion: Verify that speed mode was completed or start implementation from scratch.');
428
+ console.log('Suggestion: Verify speed mode was completed or start from scratch.');
417
429
  return;
418
430
  }
419
431
 
420
432
  if (unreadableFiles.length > 0) {
421
- console.log(`⚠️ ${unreadableFiles.length} files cannot be read - skipping them`);
433
+ console.log(`⚠️ ${unreadableFiles.length} files cannot be read - skipping`);
422
434
  }
423
435
 
424
436
  speedModeFiles = readableFiles;
@@ -452,6 +464,11 @@ Now proposing comprehensive implementation...
452
464
 
453
465
  **Move to Step 3 automatically.**
454
466
 
467
+ <!-- ═══════════════════════════════════════════════════════════════════════════
468
+ PHASE 2: USER CONFIRMATION REQUIRED
469
+ ⚡ ASYNC BOUNDARY - Must wait for user response before proceeding
470
+ ═══════════════════════════════════════════════════════════════════════════ -->
471
+
455
472
  ### Step 3: Propose and Execute Comprehensive Implementation
456
473
 
457
474
  **Two phases: Propose (get user confirmation) → Execute (autonomous)**
@@ -490,26 +507,40 @@ Sound good? I'll implement this autonomously once you confirm.
490
507
 
491
508
  If user adjusts: revise proposal and confirm again.
492
509
 
510
+ <!-- ═══════════════════════════════════════════════════════════════════════════
511
+ PHASE 3: AUTONOMOUS EXECUTION
512
+ User has confirmed - Claude Code executes iteration loop independently
513
+ ═══════════════════════════════════════════════════════════════════════════ -->
514
+
493
515
  #### Phase 2: Autonomous Execution
494
516
 
495
517
  **CRITICAL:** After user confirms, Claude Code executes autonomously - no permission needed for individual code changes.
496
518
 
497
519
  **Execution loop (with iteration limits and error handling):**
498
520
 
521
+ <!-- ┌─────────────────────────────────────────────────────────────────────────┐
522
+ │ 🔄 ITERATION LOOP: Stable Mode Scenario │
523
+ │ │
524
+ │ Progress Tracking: │
525
+ │ • Display: "Iteration X/10" at start of each cycle │
526
+ │ • Track: scenarios passing, steps passing, newly passing │
527
+ │ • Goal: Target scenario + all scenarios pass │
528
+ │ │
529
+ │ Return Points: │
530
+ │ • CHECKPOINT_ITERATION: Resume at specific iteration number │
531
+ │ • CHECKPOINT_SCENARIO: Resume with known scenario status │
532
+ │ • If session interrupted, can resume from last known iteration │
533
+ └─────────────────────────────────────────────────────────────────────────┘ -->
534
+
499
535
  ```javascript
500
- // Import test-runner utilities
536
+ // --- Imports ---
501
537
  const {
502
- MAX_ITERATIONS,
503
- TEST_TIMEOUT,
504
- runBddTestWithTimeout,
505
- runBddScenarioWithTimeout,
506
- getScenarioLineByName,
507
- parseTestProgress,
508
- extractErrors,
509
- findNewlyPassingSteps
538
+ MAX_ITERATIONS, TEST_TIMEOUT, runBddTestWithTimeout,
539
+ runBddScenarioWithTimeout, getScenarioLineByName,
540
+ parseTestProgress, extractErrors, findNewlyPassingSteps
510
541
  } = require('../../.claude/skills/speed-mode/test-runner');
511
542
 
512
- // Identify target scenario line from targetScenario.title
543
+ // --- Find scenario line number ---
513
544
  const scenarioLine = getScenarioLineByName(feature.scenario_file, targetScenario.title);
514
545
  if (!scenarioLine) {
515
546
  console.error('❌ Cannot find scenario line number for:', targetScenario.title);
@@ -520,42 +551,37 @@ let iteration = 0;
520
551
  let scenarioPasses = false;
521
552
  let previousResult = null;
522
553
 
554
+ // --- Main iteration loop ---
555
+ // 📊 PROGRESS: Iteration {iteration}/{MAX_ITERATIONS} | Scenario: {scenarioPasses ? 'PASS' : 'FAIL'}
523
556
  while (!scenarioPasses && iteration < MAX_ITERATIONS) {
524
557
  iteration++;
525
558
  console.log(`\n🔄 Iteration ${iteration}/${MAX_ITERATIONS}`);
526
559
 
527
- // 1. Modify existing files using Edit tool
560
+ // --- Make code changes ---
528
561
  try {
529
- // Add error handling, validation, error messages, etc.
530
562
  console.log('✍️ Adding error handling to [file]...');
531
563
  // ... use Edit tool ...
532
564
  console.log('✅ Updated [file]');
533
565
  } catch (editErr) {
534
566
  console.error('❌ Error modifying files:', editErr.message);
535
- console.log('Retrying with adjusted approach...');
536
567
  continue;
537
568
  }
538
569
 
539
- // 2. Run tests with timeout - only target scenario during iteration
570
+ // --- Run tests ---
540
571
  console.log('🧪 Running tests...');
541
-
542
572
  const result = await runBddScenarioWithTimeout(feature.scenario_file, scenarioLine, TEST_TIMEOUT);
543
573
 
544
- // Check for timeout
574
+ // --- Handle timeout ---
545
575
  if (result.timedOut) {
546
576
  console.error('❌ Tests timed out after 60 seconds');
547
- console.log('This might indicate an infinite loop or hung process.');
548
577
  console.log('Suggestion: Check for blocking operations or missing async/await');
549
578
  break;
550
579
  }
551
580
 
552
- // Parse results
581
+ // --- Parse and track progress ---
553
582
  const currentResult = parseTestProgress(result.stdout);
554
-
555
- // Track progress
556
583
  const newlyPassing = findNewlyPassingSteps(previousResult, currentResult);
557
584
 
558
- // Display progress
559
585
  console.log(`\n📊 Progress: ${currentResult.passed}/${currentResult.total} steps passing`);
560
586
 
561
587
  if (newlyPassing.length > 0) {
@@ -563,11 +589,11 @@ while (!scenarioPasses && iteration < MAX_ITERATIONS) {
563
589
  newlyPassing.forEach(step => console.log(` • ${step}`));
564
590
  }
565
591
 
566
- // Check if target scenario passes
592
+ // --- Check for success ---
567
593
  if (currentResult.passed === currentResult.total && currentResult.total > 0) {
568
594
  console.log('\n✅ Target scenario passing!');
569
595
 
570
- // Run full verification (all scenarios) once for regression detection
596
+ // --- Run full verification ---
571
597
  console.log('\n🔍 Running full verification (all scenarios)...');
572
598
  const fullResult = await runBddTestWithTimeout(feature.scenario_file, TEST_TIMEOUT);
573
599
 
@@ -575,31 +601,27 @@ while (!scenarioPasses && iteration < MAX_ITERATIONS) {
575
601
  console.log('⚠️ Full verification timed out');
576
602
  } else if (fullResult.exitCode !== 0) {
577
603
  const fullProgress = parseTestProgress(fullResult.stdout + fullResult.stderr);
578
- console.log(`⚠️ Full verification found regressions: ${fullProgress.total - fullProgress.passed} scenarios failing`);
579
- console.log('Continuing iterations to fix regressions...');
604
+ console.log(`⚠️ Regressions found: ${fullProgress.total - fullProgress.passed} scenarios failing`);
580
605
  scenarioPasses = false;
581
606
  } else {
582
- console.log('✅ Full verification passed - all scenarios passing!');
607
+ console.log('✅ Full verification passed!');
583
608
  scenarioPasses = true;
584
609
  }
585
610
  } else {
611
+ // --- Display errors ---
586
612
  console.log(`\n❌ ${currentResult.total - currentResult.passed} scenarios still failing`);
587
-
588
- // Extract and display errors
589
613
  const errors = extractErrors(result.stdout + result.stderr);
590
614
  if (errors.errors.length > 0) {
591
615
  console.log('\n🔧 Next failure to address:');
592
- const firstError = errors.errors[0];
593
- console.log(` Step: ${firstError.step}`);
594
- console.log(` Error: ${firstError.message}`);
616
+ console.log(` Step: ${errors.errors[0].step}`);
617
+ console.log(` Error: ${errors.errors[0].message}`);
595
618
  }
596
619
  }
597
620
 
598
- // Update previousResult for next iteration
599
621
  previousResult = currentResult;
600
622
  }
601
623
 
602
- // Error handling: Max iterations reached
624
+ // --- Handle max iterations reached ---
603
625
  if (!scenarioPasses && iteration >= MAX_ITERATIONS) {
604
626
  console.error('\n❌ Maximum iterations reached without passing scenario');
605
627
  console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
@@ -609,15 +631,7 @@ if (!scenarioPasses && iteration >= MAX_ITERATIONS) {
609
631
  console.log('• Scenario requirements may need clarification');
610
632
  console.log('• Implementation approach may need rethinking');
611
633
  console.log('• External dependencies may be missing');
612
- console.log(' Test assertions may be incorrect');
613
- console.log('\nSuggestions:');
614
- console.log('1. Review the scenario and verify it\'s achievable');
615
- console.log('2. Check test output for specific failure patterns');
616
- console.log('3. Try a different implementation approach');
617
- console.log('4. Ask for help if stuck\n');
618
-
619
- // Ask user how to proceed
620
- console.log('How would you like to proceed?');
634
+ console.log('\nHow would you like to proceed?');
621
635
  console.log(' 1. Review changes made so far');
622
636
  console.log(' 2. Try a different approach');
623
637
  console.log(' 3. Debug manually');
@@ -657,31 +671,16 @@ Implementation complete:
657
671
  • All scenarios: ✅ Passing
658
672
  ```
659
673
 
660
- **Run unit test coverage tracking:**
661
- ```javascript
662
- const { trackFeatureCoverage } = require('../../lib/coverage-tracker');
663
-
664
- // Track coverage for modified implementation files
665
- const implementationFiles = [
666
- // List of .js files modified during implementation (exclude test files)
667
- ];
668
-
669
- console.log('\n📊 Running unit test coverage...');
670
- const coverageResult = trackFeatureCoverage(implementationFiles, {
671
- coverageThreshold: 80, // 80% coverage target
672
- verbose: false
673
- });
674
-
675
- console.log(coverageResult.report);
676
-
677
- if (!coverageResult.success) {
678
- console.log('\n⚠️ Coverage below 80% threshold');
679
- console.log('Consider adding more unit tests to cover edge cases');
680
- console.log('\nTest files:');
681
- coverageResult.testFiles.forEach(f => console.log(` • ${f}`));
682
- }
674
+ **Run unit test coverage (if configured):**
675
+ ```bash
676
+ # If project has coverage configured, run it:
677
+ npm run test:coverage
678
+ # Or with Jest:
679
+ npx jest --coverage
683
680
  ```
684
681
 
682
+ Note: Coverage tracking is project-specific. Check your package.json for the appropriate coverage command.
683
+
685
684
  **Display coverage summary:**
686
685
  ```
687
686
  📊 Unit Test Coverage Report
@@ -694,45 +693,77 @@ Lines: ✅ 84.90%
694
693
  ✅ All coverage metrics meet 80% threshold
695
694
  ```
696
695
 
696
+ <!-- ═══════════════════════════════════════════════════════════════════════════
697
+ PHASE 4: COMPLETION AND ROUTING
698
+ Conditional phase - route based on project state (internal vs external)
699
+ ═══════════════════════════════════════════════════════════════════════════ -->
700
+
697
701
  ### Step 4: Check for Production Mode (When All Stable Chores Complete)
698
702
 
699
703
  **CRITICAL: This step ONLY happens when ALL stable chores for the feature are complete. Otherwise skip to marking chore as done.**
700
704
 
705
+ <!-- ┌─────────────────────────────────────────────────────────────────────────┐
706
+ │ 🔍 COMPLETION CHECK: Stable Chores │
707
+ │ │
708
+ │ Progress Tracking: │
709
+ │ • Query: Count incomplete chores for feature │
710
+ │ • Display: "X chores remaining" or "All complete" │
711
+ │ • Action: Route based on project state (internal vs external) │
712
+ │ │
713
+ │ Return Point: │
714
+ │ • CHECKPOINT_CHORE_COUNT: Known incomplete count from last check │
715
+ │ • CHECKPOINT_PROJECT_STATE: Known project state for routing │
716
+ │ • If session interrupted, re-query to get current counts │
717
+ └─────────────────────────────────────────────────────────────────────────┘ -->
718
+
701
719
  **Check completion status and project state:**
702
720
  ```javascript
721
+ // --- Imports ---
703
722
  const { getDb } = require('../../lib/database');
704
723
  const { getCurrentWork } = require('../../lib/current-work');
705
724
 
706
- const currentWork = getCurrentWork();
707
- const featureId = currentWork.parent_id;
708
-
709
- const db = getDb();
710
- db.get(`
711
- SELECT COUNT(*) as incomplete_count
712
- FROM work_items
713
- WHERE parent_id = ?
714
- AND type = 'chore'
715
- AND status NOT IN ('done', 'cancelled')
716
- `, [featureId], (err, result) => {
725
+ (async () => {
726
+ // --- Get current work context ---
727
+ const currentWork = await getCurrentWork();
728
+ const featureId = currentWork.parent_id;
729
+ const db = getDb();
730
+
731
+ // --- Query for incomplete chores ---
732
+ const result = await new Promise((resolve, reject) => {
733
+ db.get(`
734
+ SELECT COUNT(*) as incomplete_count
735
+ FROM work_items
736
+ WHERE parent_id = ?
737
+ AND type = 'chore'
738
+ AND status NOT IN ('done', 'cancelled')
739
+ `, [featureId], (err, row) => {
740
+ if (err) reject(err);
741
+ else resolve(row);
742
+ });
743
+ });
744
+
745
+ // --- Handle all chores complete ---
717
746
  if (result.incomplete_count === 0) {
718
- // All stable chores done - check project state
719
- db.get('SELECT project_state FROM project_config WHERE id = 1', (err, config) => {
720
- const projectState = config?.project_state || 'internal';
721
-
722
- if (projectState === 'internal') {
723
- // Internal project - stable mode is complete
724
- console.log('Internal project - no production mode needed');
725
- } else {
726
- // External project - invoke production-mode skill
727
- console.log('External project - auto-generating production chores from standards');
728
- }
747
+ const config = await new Promise((resolve, reject) => {
748
+ db.get('SELECT project_state FROM project_config WHERE id = 1', (err, row) => {
749
+ if (err) reject(err);
750
+ else resolve(row);
751
+ });
729
752
  });
753
+ const projectState = config?.project_state || 'internal';
754
+
755
+ // --- Route based on project state ---
756
+ if (projectState === 'internal') {
757
+ console.log('Internal project - no production mode needed');
758
+ } else {
759
+ console.log('External project - auto-generating production chores');
760
+ }
730
761
  } else {
731
- // More chores remain - just mark this one done
732
762
  console.log(`\n📋 ${result.incomplete_count} stable mode chores remaining`);
733
763
  }
764
+
734
765
  db.close();
735
- });
766
+ })();
736
767
  ```
737
768
 
738
769
  **CRITICAL: Check project state to determine next step:**