popeye-cli 1.5.0 → 1.7.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.
Files changed (195) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +184 -31
  3. package/dist/cli/commands/create.d.ts.map +1 -1
  4. package/dist/cli/commands/create.js +54 -4
  5. package/dist/cli/commands/create.js.map +1 -1
  6. package/dist/cli/interactive.d.ts +29 -0
  7. package/dist/cli/interactive.d.ts.map +1 -1
  8. package/dist/cli/interactive.js +90 -7
  9. package/dist/cli/interactive.js.map +1 -1
  10. package/dist/generators/all.d.ts +4 -1
  11. package/dist/generators/all.d.ts.map +1 -1
  12. package/dist/generators/all.js +36 -316
  13. package/dist/generators/all.js.map +1 -1
  14. package/dist/generators/doc-parser.d.ts +18 -3
  15. package/dist/generators/doc-parser.d.ts.map +1 -1
  16. package/dist/generators/doc-parser.js +81 -10
  17. package/dist/generators/doc-parser.js.map +1 -1
  18. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  19. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  20. package/dist/generators/frontend-design-analyzer.js +208 -0
  21. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  22. package/dist/generators/shared-packages.d.ts +45 -0
  23. package/dist/generators/shared-packages.d.ts.map +1 -0
  24. package/dist/generators/shared-packages.js +456 -0
  25. package/dist/generators/shared-packages.js.map +1 -0
  26. package/dist/generators/templates/index.d.ts +4 -0
  27. package/dist/generators/templates/index.d.ts.map +1 -1
  28. package/dist/generators/templates/index.js +4 -0
  29. package/dist/generators/templates/index.js.map +1 -1
  30. package/dist/generators/templates/website-components.d.ts.map +1 -1
  31. package/dist/generators/templates/website-components.js +36 -11
  32. package/dist/generators/templates/website-components.js.map +1 -1
  33. package/dist/generators/templates/website-config.d.ts +15 -1
  34. package/dist/generators/templates/website-config.d.ts.map +1 -1
  35. package/dist/generators/templates/website-config.js +155 -13
  36. package/dist/generators/templates/website-config.js.map +1 -1
  37. package/dist/generators/templates/website-landing.d.ts +24 -0
  38. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  39. package/dist/generators/templates/website-landing.js +276 -0
  40. package/dist/generators/templates/website-landing.js.map +1 -0
  41. package/dist/generators/templates/website-layout.d.ts +42 -0
  42. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  43. package/dist/generators/templates/website-layout.js +408 -0
  44. package/dist/generators/templates/website-layout.js.map +1 -0
  45. package/dist/generators/templates/website-pricing.d.ts +11 -0
  46. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  47. package/dist/generators/templates/website-pricing.js +313 -0
  48. package/dist/generators/templates/website-pricing.js.map +1 -0
  49. package/dist/generators/templates/website-sections.d.ts +102 -0
  50. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  51. package/dist/generators/templates/website-sections.js +444 -0
  52. package/dist/generators/templates/website-sections.js.map +1 -0
  53. package/dist/generators/templates/website.d.ts +10 -50
  54. package/dist/generators/templates/website.d.ts.map +1 -1
  55. package/dist/generators/templates/website.js +12 -788
  56. package/dist/generators/templates/website.js.map +1 -1
  57. package/dist/generators/website-content-scanner.d.ts +37 -0
  58. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  59. package/dist/generators/website-content-scanner.js +165 -0
  60. package/dist/generators/website-content-scanner.js.map +1 -0
  61. package/dist/generators/website-context.d.ts +38 -2
  62. package/dist/generators/website-context.d.ts.map +1 -1
  63. package/dist/generators/website-context.js +179 -19
  64. package/dist/generators/website-context.js.map +1 -1
  65. package/dist/generators/website-debug.d.ts +68 -0
  66. package/dist/generators/website-debug.d.ts.map +1 -0
  67. package/dist/generators/website-debug.js +93 -0
  68. package/dist/generators/website-debug.js.map +1 -0
  69. package/dist/generators/website.d.ts +2 -0
  70. package/dist/generators/website.d.ts.map +1 -1
  71. package/dist/generators/website.js +66 -4
  72. package/dist/generators/website.js.map +1 -1
  73. package/dist/generators/workspace-root.d.ts +27 -0
  74. package/dist/generators/workspace-root.d.ts.map +1 -0
  75. package/dist/generators/workspace-root.js +100 -0
  76. package/dist/generators/workspace-root.js.map +1 -0
  77. package/dist/state/index.d.ts +8 -0
  78. package/dist/state/index.d.ts.map +1 -1
  79. package/dist/state/index.js +11 -0
  80. package/dist/state/index.js.map +1 -1
  81. package/dist/types/consensus.d.ts +3 -0
  82. package/dist/types/consensus.d.ts.map +1 -1
  83. package/dist/types/consensus.js +1 -0
  84. package/dist/types/consensus.js.map +1 -1
  85. package/dist/types/index.d.ts +1 -0
  86. package/dist/types/index.d.ts.map +1 -1
  87. package/dist/types/index.js +2 -0
  88. package/dist/types/index.js.map +1 -1
  89. package/dist/types/tester.d.ts +138 -0
  90. package/dist/types/tester.d.ts.map +1 -0
  91. package/dist/types/tester.js +110 -0
  92. package/dist/types/tester.js.map +1 -0
  93. package/dist/types/workflow.d.ts +151 -0
  94. package/dist/types/workflow.d.ts.map +1 -1
  95. package/dist/types/workflow.js +14 -0
  96. package/dist/types/workflow.js.map +1 -1
  97. package/dist/upgrade/handlers.d.ts +15 -0
  98. package/dist/upgrade/handlers.d.ts.map +1 -1
  99. package/dist/upgrade/handlers.js +52 -0
  100. package/dist/upgrade/handlers.js.map +1 -1
  101. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  102. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  103. package/dist/workflow/auto-fix-bundler.js +320 -0
  104. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  105. package/dist/workflow/auto-fix.d.ts.map +1 -1
  106. package/dist/workflow/auto-fix.js +10 -3
  107. package/dist/workflow/auto-fix.js.map +1 -1
  108. package/dist/workflow/execution-mode.js +2 -2
  109. package/dist/workflow/execution-mode.js.map +1 -1
  110. package/dist/workflow/index.d.ts +2 -0
  111. package/dist/workflow/index.d.ts.map +1 -1
  112. package/dist/workflow/index.js +13 -0
  113. package/dist/workflow/index.js.map +1 -1
  114. package/dist/workflow/overview.d.ts.map +1 -1
  115. package/dist/workflow/overview.js +4 -0
  116. package/dist/workflow/overview.js.map +1 -1
  117. package/dist/workflow/plan-mode.d.ts +4 -3
  118. package/dist/workflow/plan-mode.d.ts.map +1 -1
  119. package/dist/workflow/plan-mode.js +69 -5
  120. package/dist/workflow/plan-mode.js.map +1 -1
  121. package/dist/workflow/task-workflow.d.ts +5 -0
  122. package/dist/workflow/task-workflow.d.ts.map +1 -1
  123. package/dist/workflow/task-workflow.js +172 -6
  124. package/dist/workflow/task-workflow.js.map +1 -1
  125. package/dist/workflow/tester.d.ts +120 -0
  126. package/dist/workflow/tester.d.ts.map +1 -0
  127. package/dist/workflow/tester.js +589 -0
  128. package/dist/workflow/tester.js.map +1 -0
  129. package/dist/workflow/website-strategy.d.ts +9 -0
  130. package/dist/workflow/website-strategy.d.ts.map +1 -1
  131. package/dist/workflow/website-strategy.js +73 -1
  132. package/dist/workflow/website-strategy.js.map +1 -1
  133. package/dist/workflow/website-updater.d.ts.map +1 -1
  134. package/dist/workflow/website-updater.js +15 -4
  135. package/dist/workflow/website-updater.js.map +1 -1
  136. package/dist/workflow/workflow-logger.d.ts +1 -1
  137. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  138. package/dist/workflow/workflow-logger.js.map +1 -1
  139. package/package.json +1 -1
  140. package/src/cli/commands/create.ts +58 -4
  141. package/src/cli/interactive.ts +96 -7
  142. package/src/generators/all.ts +44 -332
  143. package/src/generators/doc-parser.ts +87 -10
  144. package/src/generators/frontend-design-analyzer.ts +261 -0
  145. package/src/generators/shared-packages.ts +500 -0
  146. package/src/generators/templates/index.ts +4 -0
  147. package/src/generators/templates/website-components.ts +36 -11
  148. package/src/generators/templates/website-config.ts +166 -13
  149. package/src/generators/templates/website-landing.ts +331 -0
  150. package/src/generators/templates/website-layout.ts +443 -0
  151. package/src/generators/templates/website-pricing.ts +330 -0
  152. package/src/generators/templates/website-sections.ts +541 -0
  153. package/src/generators/templates/website.ts +38 -851
  154. package/src/generators/website-content-scanner.ts +208 -0
  155. package/src/generators/website-context.ts +248 -20
  156. package/src/generators/website-debug.ts +130 -0
  157. package/src/generators/website.ts +71 -3
  158. package/src/generators/workspace-root.ts +113 -0
  159. package/src/state/index.ts +15 -0
  160. package/src/types/consensus.ts +3 -0
  161. package/src/types/index.ts +21 -0
  162. package/src/types/tester.ts +136 -0
  163. package/src/types/workflow.ts +32 -0
  164. package/src/upgrade/handlers.ts +65 -0
  165. package/src/workflow/auto-fix-bundler.ts +392 -0
  166. package/src/workflow/auto-fix.ts +11 -3
  167. package/src/workflow/execution-mode.ts +2 -2
  168. package/src/workflow/index.ts +13 -0
  169. package/src/workflow/overview.ts +6 -0
  170. package/src/workflow/plan-mode.ts +81 -7
  171. package/src/workflow/task-workflow.ts +227 -5
  172. package/src/workflow/tester.ts +723 -0
  173. package/src/workflow/website-strategy.ts +75 -1
  174. package/src/workflow/website-updater.ts +17 -6
  175. package/src/workflow/workflow-logger.ts +2 -0
  176. package/tests/cli/project-naming.test.ts +136 -0
  177. package/tests/generators/doc-parser.test.ts +121 -0
  178. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  179. package/tests/generators/quality-gate.test.ts +183 -0
  180. package/tests/generators/shared-packages.test.ts +83 -0
  181. package/tests/generators/website-components.test.ts +1 -1
  182. package/tests/generators/website-config.test.ts +84 -0
  183. package/tests/generators/website-content-scanner.test.ts +181 -0
  184. package/tests/generators/website-context.test.ts +109 -0
  185. package/tests/generators/website-debug.test.ts +77 -0
  186. package/tests/generators/website-landing.test.ts +188 -0
  187. package/tests/generators/website-pricing.test.ts +98 -0
  188. package/tests/generators/website-sections.test.ts +245 -0
  189. package/tests/generators/workspace-root.test.ts +105 -0
  190. package/tests/types/tester.test.ts +174 -0
  191. package/tests/upgrade/handlers.test.ts +162 -0
  192. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  193. package/tests/workflow/plan-mode.test.ts +111 -1
  194. package/tests/workflow/tester.test.ts +401 -0
  195. package/tests/workflow/website-strategy.test.ts +55 -0
@@ -8,6 +8,7 @@ import path from 'node:path';
8
8
  import { isWorkspace } from '../types/project.js';
9
9
  import type { ProjectState, Task, Milestone } from '../types/workflow.js';
10
10
  import type { ConsensusConfig } from '../types/consensus.js';
11
+ import type { TestRunReview } from '../types/tester.js';
11
12
  import { createPlan as claudeCreatePlan } from '../adapters/claude.js';
12
13
  import {
13
14
  loadProject,
@@ -16,6 +17,14 @@ import {
16
17
  import { iterateUntilConsensus, runOptimizedConsensusProcess, type ConsensusProcessResult } from './consensus.js';
17
18
  import { executeTask as executeTaskCode, handleTestFailure } from './execution-mode.js';
18
19
  import { runTests, testsExist, getTestSummary, type TestResult } from './test-runner.js';
20
+ import {
21
+ isQaEnabled,
22
+ runTestPlanningPhase,
23
+ runTestReviewPhase,
24
+ createTesterFixPlan,
25
+ documentTestPlan as documentQaTestPlan,
26
+ documentTestReview,
27
+ } from './tester.js';
19
28
 
20
29
  /**
21
30
  * Options for task workflow
@@ -38,6 +47,10 @@ export interface TaskWorkflowResult {
38
47
  error?: string;
39
48
  /** True if workflow paused due to rate limiting (not a failure) */
40
49
  rateLimitPaused?: boolean;
50
+ /** Consensus result for the QA test plan (when QA enabled) */
51
+ testPlanConsensusResult?: ConsensusProcessResult;
52
+ /** Tester's post-run review (when QA enabled) */
53
+ testRunReview?: TestRunReview;
41
54
  }
42
55
 
43
56
  /**
@@ -81,15 +94,14 @@ Create a detailed implementation plan for the following task:
81
94
  ## Task: ${task.name}
82
95
  ${task.description}
83
96
 
84
- ${task.testPlan ? `## Test Requirements\n${task.testPlan}` : ''}
85
-
86
97
  Please provide:
87
98
  1. **Implementation Steps**: Specific code changes needed
88
99
  2. **Files to Create/Modify**: List all files that will be touched
89
100
  3. **Dependencies**: Any packages or modules needed
90
101
  4. **Acceptance Criteria**: How to verify the task is complete
91
- 5. **Test Plan**: Specific tests to write
102
+ 5. **Integration Points**: How this code connects with existing modules
92
103
 
104
+ Do NOT include test planning -- a dedicated QA engineer will handle test design separately.
93
105
  Be specific and actionable. This plan will be reviewed for consensus before implementation.
94
106
  `.trim();
95
107
 
@@ -369,7 +381,9 @@ export async function runTaskWorkflow(
369
381
 
370
382
  // Check if we're resuming from a previous attempt
371
383
  const hasApprovedPlan = task.consensusApproved && task.plan;
384
+ const hasApprovedTestPlan = task.qaTestPlanApproved && task.qaTestPlanText;
372
385
  const hasCompletedImplementation = task.implementationComplete;
386
+ const qaActive = isQaEnabled(state);
373
387
 
374
388
  // Mark task as in-progress
375
389
  state = await updateTaskInState(projectDir, task.id, {
@@ -509,16 +523,83 @@ Task: ${task.name}
509
523
  }
510
524
 
511
525
  // ============================================
512
- // PHASE 3: Implement the Task (skip if already complete)
526
+ // PHASE 3-4: Tester creates TestPlan + Consensus (only if QA enabled)
527
+ // ============================================
528
+ let testPlanConsensusResult: ConsensusProcessResult | undefined;
529
+
530
+ if (qaActive && !hasApprovedTestPlan) {
531
+ onProgress?.('test-planning', `Tester is designing test plan for: ${task.name}`);
532
+
533
+ const testPlanResult = await runTestPlanningPhase(
534
+ task, milestone, state, consensusResult.bestPlan,
535
+ { projectDir, consensusConfig, onProgress },
536
+ );
537
+
538
+ if (testPlanResult.error) {
539
+ state = await updateTaskInState(projectDir, task.id, {
540
+ status: 'failed',
541
+ error: testPlanResult.error,
542
+ });
543
+ return {
544
+ success: false,
545
+ task: { ...task, status: 'failed' },
546
+ consensusResult,
547
+ error: testPlanResult.error,
548
+ };
549
+ }
550
+
551
+ testPlanConsensusResult = testPlanResult.consensusResult;
552
+
553
+ // Document and persist test plan
554
+ const qaDocPath = await documentQaTestPlan(
555
+ projectDir, milestone, task, testPlanResult.testPlanText, testPlanResult.consensusResult,
556
+ );
557
+
558
+ state = await updateTaskInState(projectDir, task.id, {
559
+ qaTestPlanText: testPlanResult.testPlanText,
560
+ qaTestPlanParsed: testPlanResult.testPlanParsed ?? undefined,
561
+ qaTestPlanScore: testPlanResult.consensusResult.finalScore,
562
+ qaTestPlanIterations: testPlanResult.consensusResult.totalIterations,
563
+ qaTestPlanApproved: testPlanResult.consensusResult.approved,
564
+ qaTestPlanDoc: qaDocPath,
565
+ });
566
+
567
+ if (!testPlanResult.consensusResult.approved) {
568
+ onProgress?.('test-planning', `Test plan consensus not reached: ${testPlanResult.consensusResult.finalScore}%`);
569
+ state = await updateTaskInState(projectDir, task.id, {
570
+ status: 'failed',
571
+ error: `Test plan consensus not reached: ${testPlanResult.consensusResult.finalScore}%`,
572
+ });
573
+ return {
574
+ success: false,
575
+ task: { ...task, status: 'failed' },
576
+ consensusResult,
577
+ testPlanConsensusResult,
578
+ error: `Test plan not approved. Score: ${testPlanResult.consensusResult.finalScore}%`,
579
+ };
580
+ }
581
+
582
+ onProgress?.('test-planning', `Test plan approved with ${testPlanResult.consensusResult.finalScore}% consensus`);
583
+ } else if (qaActive && hasApprovedTestPlan) {
584
+ onProgress?.('test-planning', `Using existing approved test plan for: ${task.name} (Score: ${task.qaTestPlanScore}%)`);
585
+ }
586
+
587
+ // ============================================
588
+ // PHASE 5: Implement the Task (skip if already complete)
513
589
  // ============================================
514
590
  if (hasCompletedImplementation) {
515
591
  onProgress?.('task-implement', `Implementation already complete for: ${task.name}, skipping to tests...`);
516
592
  } else {
517
593
  onProgress?.('task-implement', `Implementing task: ${task.name}`);
518
594
 
595
+ // When QA is active, provide both approved plans as context
596
+ const implementationContext = (qaActive && task.qaTestPlanText)
597
+ ? `${consensusResult.bestPlan}\n\n## Approved Test Plan\n${task.qaTestPlanText}`
598
+ : consensusResult.bestPlan;
599
+
519
600
  const implementResult = await executeTaskCode(
520
601
  task,
521
- consensusResult.bestPlan, // Use the approved plan as context
602
+ implementationContext,
522
603
  projectDir,
523
604
  (msg) => onProgress?.('task-implement', msg)
524
605
  );
@@ -738,10 +819,150 @@ Phase: Test failure fix (attempt ${retries}/${maxRetries})
738
819
  task: { ...task, status: 'failed', testsPassed: false },
739
820
  consensusResult,
740
821
  testResult,
822
+ testPlanConsensusResult,
741
823
  error: `Tests failed: ${getTestSummary(testResult)}`,
742
824
  };
743
825
  }
744
826
 
827
+ // ============================================
828
+ // PHASE 7: Tester reviews test results (only if QA enabled)
829
+ // ============================================
830
+ if (qaActive && task.qaTestPlanText && testResult) {
831
+ onProgress?.('test-review', `Tester is reviewing test results for: ${task.name}`);
832
+
833
+ const review = await runTestReviewPhase(
834
+ task, task.qaTestPlanText, testResult, state, onProgress,
835
+ );
836
+
837
+ // Document the review
838
+ const reviewDocPath = await documentTestReview(projectDir, milestone, task, review);
839
+
840
+ state = await updateTaskInState(projectDir, task.id, {
841
+ qaVerdict: review.verdict,
842
+ qaReviewNotes: review.summary,
843
+ qaReviewDoc: reviewDocPath,
844
+ });
845
+
846
+ if (review.verdict === 'FAIL') {
847
+ onProgress?.('test-review', `Tester verdict: FAIL - ${review.summary}`);
848
+
849
+ // Tester creates a fix plan -> consensus -> coder implements -> re-review loop
850
+ const fixPlanText = await createTesterFixPlan(
851
+ task, task.qaTestPlanText, testResult, review, state, onProgress,
852
+ );
853
+
854
+ // Get consensus on the fix plan
855
+ const fixContext = `Project: ${state.name}\nLanguage: ${state.language}\nMilestone: ${milestone.name}\nTask: ${task.name}\nPhase: QA Fix Plan`;
856
+ const useOptimized = consensusConfig?.useOptimizedConsensus !== false;
857
+ let fixConsensus: ConsensusProcessResult;
858
+
859
+ if (useOptimized) {
860
+ fixConsensus = await runOptimizedConsensusProcess(
861
+ fixPlanText, fixContext,
862
+ {
863
+ projectDir, config: consensusConfig,
864
+ milestoneId: milestone.id, milestoneName: milestone.name,
865
+ taskId: task.id, taskName: `${task.name} - QA Fix`,
866
+ parallelReviews: true, isFullstack: isWorkspace(state.language),
867
+ onIteration: (iteration, result) => {
868
+ onProgress?.('test-review', `QA fix consensus iteration ${iteration}: ${result.score}%`);
869
+ },
870
+ onProgress,
871
+ },
872
+ ) as ConsensusProcessResult;
873
+ } else {
874
+ fixConsensus = await iterateUntilConsensus(
875
+ fixPlanText, fixContext,
876
+ {
877
+ projectDir, config: consensusConfig,
878
+ isFullstack: isWorkspace(state.language), language: state.language,
879
+ onIteration: (iteration, result) => {
880
+ onProgress?.('test-review', `QA fix consensus iteration ${iteration}: ${result.score}%`);
881
+ },
882
+ onProgress,
883
+ },
884
+ ) as ConsensusProcessResult;
885
+ }
886
+
887
+ if (!fixConsensus.approved) {
888
+ state = await updateTaskInState(projectDir, task.id, {
889
+ status: 'failed',
890
+ error: `QA fix plan not approved (${fixConsensus.finalScore}%)`,
891
+ });
892
+ return {
893
+ success: false,
894
+ task: { ...task, status: 'failed' },
895
+ consensusResult,
896
+ testResult,
897
+ testPlanConsensusResult,
898
+ testRunReview: review,
899
+ error: `QA fix plan not approved. Score: ${fixConsensus.finalScore}%`,
900
+ };
901
+ }
902
+
903
+ // Implement the fix
904
+ onProgress?.('test-review', 'Implementing QA-approved fix...');
905
+ const fixResult = await handleTestFailure(
906
+ task, testResult, fixConsensus.bestPlan, projectDir,
907
+ (msg) => onProgress?.('test-review', msg),
908
+ );
909
+
910
+ if (!fixResult.success) {
911
+ state = await updateTaskInState(projectDir, task.id, {
912
+ status: 'failed',
913
+ error: `QA fix implementation failed: ${fixResult.error}`,
914
+ });
915
+ return {
916
+ success: false,
917
+ task: { ...task, status: 'failed' },
918
+ consensusResult,
919
+ testResult,
920
+ testPlanConsensusResult,
921
+ testRunReview: review,
922
+ error: `QA fix failed: ${fixResult.error}`,
923
+ };
924
+ }
925
+
926
+ // Re-run tests after fix
927
+ onProgress?.('test-review', 'Re-running tests after QA fix...');
928
+ const reTestResult = await runTests(projectDir, state.language);
929
+
930
+ // Tester re-reviews
931
+ const reReview = await runTestReviewPhase(
932
+ task, task.qaTestPlanText!, reTestResult, state, onProgress,
933
+ );
934
+
935
+ const reReviewDocPath = await documentTestReview(projectDir, milestone, task, reReview);
936
+ state = await updateTaskInState(projectDir, task.id, {
937
+ qaVerdict: reReview.verdict,
938
+ qaReviewNotes: reReview.summary,
939
+ qaReviewDoc: reReviewDocPath,
940
+ });
941
+
942
+ if (reReview.verdict === 'FAIL') {
943
+ state = await updateTaskInState(projectDir, task.id, {
944
+ status: 'failed',
945
+ testsPassed: false,
946
+ error: `QA re-review: FAIL - ${reReview.summary}`,
947
+ });
948
+ return {
949
+ success: false,
950
+ task: { ...task, status: 'failed', testsPassed: false },
951
+ consensusResult,
952
+ testResult: reTestResult,
953
+ testPlanConsensusResult,
954
+ testRunReview: reReview,
955
+ error: `QA verdict: FAIL after fix attempt`,
956
+ };
957
+ }
958
+
959
+ // PASS or PASS_WITH_NOTES after fix
960
+ onProgress?.('test-review', `Tester re-review verdict: ${reReview.verdict}`);
961
+ } else {
962
+ onProgress?.('test-review', `Tester verdict: ${review.verdict}`);
963
+ }
964
+ }
965
+
745
966
  // Mark task as complete
746
967
  state = await updateTaskInState(projectDir, task.id, {
747
968
  status: 'complete',
@@ -753,6 +974,7 @@ Phase: Test failure fix (attempt ${retries}/${maxRetries})
753
974
  task: { ...task, status: 'complete', testsPassed: true },
754
975
  consensusResult,
755
976
  testResult,
977
+ testPlanConsensusResult,
756
978
  };
757
979
  }
758
980