popeye-cli 1.6.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.
- package/README.md +139 -28
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +1 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/tester.d.ts +138 -0
- package/dist/types/tester.d.ts.map +1 -0
- package/dist/types/tester.js +110 -0
- package/dist/types/tester.js.map +1 -0
- package/dist/types/workflow.d.ts +145 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +12 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/execution-mode.js +2 -2
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/task-workflow.d.ts +5 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +172 -6
- package/dist/workflow/task-workflow.js.map +1 -1
- package/dist/workflow/tester.d.ts +120 -0
- package/dist/workflow/tester.d.ts.map +1 -0
- package/dist/workflow/tester.js +589 -0
- package/dist/workflow/tester.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/state/index.ts +1 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/index.ts +21 -0
- package/src/types/tester.ts +136 -0
- package/src/types/workflow.ts +26 -0
- package/src/workflow/execution-mode.ts +2 -2
- package/src/workflow/index.ts +1 -0
- package/src/workflow/task-workflow.ts +227 -5
- package/src/workflow/tester.ts +723 -0
- package/src/workflow/workflow-logger.ts +2 -0
- package/tests/types/tester.test.ts +174 -0
- package/tests/workflow/tester.test.ts +401 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-logger.js","sourceRoot":"","sources":["../../src/workflow/workflow-logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-logger.js","sourceRoot":"","sources":["../../src/workflow/workflow-logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/state/index.ts
CHANGED
package/src/types/consensus.ts
CHANGED
|
@@ -91,6 +91,8 @@ export interface ConsensusConfig {
|
|
|
91
91
|
additionalReviewers?: AIProvider[];
|
|
92
92
|
/** Custom reviewer persona for domain-specific reviews (e.g., marketing strategist for website projects) */
|
|
93
93
|
reviewerPersona?: string;
|
|
94
|
+
/** Consensus threshold for test plans (default: 90, lower than code plan threshold) */
|
|
95
|
+
testPlanThreshold?: number;
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
/**
|
|
@@ -153,6 +155,7 @@ export const ConsensusConfigSchema = z.object({
|
|
|
153
155
|
temperature: z.number().min(0).max(2).default(0.3),
|
|
154
156
|
maxTokens: z.number().min(100).max(32000).default(4096),
|
|
155
157
|
reviewerPersona: z.string().optional(),
|
|
158
|
+
testPlanThreshold: z.number().min(0).max(100).optional(),
|
|
156
159
|
});
|
|
157
160
|
|
|
158
161
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -80,6 +80,27 @@ export {
|
|
|
80
80
|
type ConsensusTrackingRecord,
|
|
81
81
|
} from './consensus.js';
|
|
82
82
|
|
|
83
|
+
// Tester (QA) types
|
|
84
|
+
export {
|
|
85
|
+
TestVerdictSchema,
|
|
86
|
+
TestScopeSchema,
|
|
87
|
+
TestCommandSchema,
|
|
88
|
+
TestCaseSchema,
|
|
89
|
+
TestPlanOutputSchema,
|
|
90
|
+
TestRunReviewSchema,
|
|
91
|
+
FixStepSchema,
|
|
92
|
+
TestFixPlanSchema,
|
|
93
|
+
type TestVerdict,
|
|
94
|
+
type TestScope,
|
|
95
|
+
type TestCommand,
|
|
96
|
+
type TestCase,
|
|
97
|
+
type TestPlanOutput,
|
|
98
|
+
type TestRunReview,
|
|
99
|
+
type FixStep,
|
|
100
|
+
type TestFixPlan,
|
|
101
|
+
type DiscoveredTestCommands,
|
|
102
|
+
} from './tester.js';
|
|
103
|
+
|
|
83
104
|
// CLI types
|
|
84
105
|
export {
|
|
85
106
|
EXIT_CODES,
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tester (QA) skill type definitions
|
|
3
|
+
* Defines test planning, review, and fix plan structures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Test verdict from the Tester's review
|
|
10
|
+
*/
|
|
11
|
+
export const TestVerdictSchema = z.enum(['PASS', 'PASS_WITH_NOTES', 'FAIL']);
|
|
12
|
+
export type TestVerdict = z.infer<typeof TestVerdictSchema>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Scope components that a test plan can cover
|
|
16
|
+
*/
|
|
17
|
+
export const TestScopeSchema = z.enum(['frontend', 'backend', 'db', 'infra']);
|
|
18
|
+
export type TestScope = z.infer<typeof TestScopeSchema>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A structured test command to execute
|
|
22
|
+
*/
|
|
23
|
+
export const TestCommandSchema = z.object({
|
|
24
|
+
/** The shell command to run */
|
|
25
|
+
command: z.string().min(1),
|
|
26
|
+
/** Working directory (relative to project root) */
|
|
27
|
+
cwd: z.string().optional(),
|
|
28
|
+
/** Human-readable purpose of this command */
|
|
29
|
+
purpose: z.string().min(1),
|
|
30
|
+
/** Whether this command must pass for the test run to succeed */
|
|
31
|
+
required: z.boolean(),
|
|
32
|
+
});
|
|
33
|
+
export type TestCommand = z.infer<typeof TestCommandSchema>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Individual test case in the test matrix
|
|
37
|
+
*/
|
|
38
|
+
export const TestCaseSchema = z.object({
|
|
39
|
+
/** Unique identifier within the test plan */
|
|
40
|
+
id: z.string().min(1),
|
|
41
|
+
/** Category: unit, integration, e2e, smoke, lint, build */
|
|
42
|
+
category: z.string().min(1),
|
|
43
|
+
/** Human-readable description of what is being tested */
|
|
44
|
+
description: z.string().min(1),
|
|
45
|
+
/** What must be true for this test to pass */
|
|
46
|
+
acceptanceCriteria: z.string().min(1),
|
|
47
|
+
/** What evidence (log output, report) is needed to verify */
|
|
48
|
+
evidenceRequired: z.string().min(1),
|
|
49
|
+
/** Priority: critical, high, medium, low */
|
|
50
|
+
priority: z.enum(['critical', 'high', 'medium', 'low']),
|
|
51
|
+
});
|
|
52
|
+
export type TestCase = z.infer<typeof TestCaseSchema>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Structured test plan output from the Tester
|
|
56
|
+
*/
|
|
57
|
+
export const TestPlanOutputSchema = z.object({
|
|
58
|
+
/** What risks this test plan targets */
|
|
59
|
+
summary: z.string().min(1),
|
|
60
|
+
/** Components covered by this plan */
|
|
61
|
+
scope: z.array(TestScopeSchema).min(1),
|
|
62
|
+
/** Matrix of test cases with acceptance criteria */
|
|
63
|
+
testMatrix: z.array(TestCaseSchema).min(1),
|
|
64
|
+
/** Exact commands to execute (with cwd, purpose, required flag) */
|
|
65
|
+
commands: z.array(TestCommandSchema).min(1),
|
|
66
|
+
/** Top risks this test plan focuses on (3-7 items) */
|
|
67
|
+
riskFocus: z.array(z.string().min(1)).min(1),
|
|
68
|
+
/** What evidence (logs, reports) to capture */
|
|
69
|
+
evidenceRequired: z.array(z.string().min(1)).min(1),
|
|
70
|
+
/** Minimum verification always present: build, lint, smoke */
|
|
71
|
+
minimumVerification: z.array(z.string().min(1)).min(1),
|
|
72
|
+
/** Rationale if tester decides no custom tests are needed (min verification still applies) */
|
|
73
|
+
noTestsRationale: z.string().optional(),
|
|
74
|
+
});
|
|
75
|
+
export type TestPlanOutput = z.infer<typeof TestPlanOutputSchema>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Post-run review from the Tester
|
|
79
|
+
*/
|
|
80
|
+
export const TestRunReviewSchema = z.object({
|
|
81
|
+
/** Overall verdict */
|
|
82
|
+
verdict: TestVerdictSchema,
|
|
83
|
+
/** Summary of the review */
|
|
84
|
+
summary: z.string().min(1),
|
|
85
|
+
/** List of evidence that was checked */
|
|
86
|
+
evidenceReviewed: z.array(z.string().min(1)).min(1),
|
|
87
|
+
/** Specific failures found (empty array if PASS) */
|
|
88
|
+
failures: z.array(z.string()),
|
|
89
|
+
/** Missing evidence or coverage gaps */
|
|
90
|
+
gaps: z.array(z.string()),
|
|
91
|
+
/** Recommendations for improvement */
|
|
92
|
+
recommendations: z.array(z.string()),
|
|
93
|
+
/** Whether this verdict requires consensus (true if FAIL) */
|
|
94
|
+
requiresConsensus: z.boolean(),
|
|
95
|
+
});
|
|
96
|
+
export type TestRunReview = z.infer<typeof TestRunReviewSchema>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Individual fix step in a TestFixPlan
|
|
100
|
+
*/
|
|
101
|
+
export const FixStepSchema = z.object({
|
|
102
|
+
/** File to modify */
|
|
103
|
+
file: z.string().min(1),
|
|
104
|
+
/** Description of the change */
|
|
105
|
+
change: z.string().min(1),
|
|
106
|
+
/** Why this change is needed */
|
|
107
|
+
reason: z.string().min(1),
|
|
108
|
+
});
|
|
109
|
+
export type FixStep = z.infer<typeof FixStepSchema>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Fix plan proposed by the Tester when tests fail
|
|
113
|
+
*/
|
|
114
|
+
export const TestFixPlanSchema = z.object({
|
|
115
|
+
/** Which acceptance criteria failed */
|
|
116
|
+
failedCriteria: z.array(z.string().min(1)).min(1),
|
|
117
|
+
/** Root cause analysis from the Tester */
|
|
118
|
+
rootCauseAnalysis: z.string().min(1),
|
|
119
|
+
/** Ordered steps to fix the failures */
|
|
120
|
+
fixSteps: z.array(FixStepSchema).min(1),
|
|
121
|
+
/** Risks of introducing regressions */
|
|
122
|
+
regressionRisks: z.array(z.string()),
|
|
123
|
+
/** Strategy for re-testing after fix */
|
|
124
|
+
retestStrategy: z.string().min(1),
|
|
125
|
+
});
|
|
126
|
+
export type TestFixPlan = z.infer<typeof TestFixPlanSchema>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Discovered test infrastructure for a project
|
|
130
|
+
*/
|
|
131
|
+
export interface DiscoveredTestCommands {
|
|
132
|
+
testCmd: string | null;
|
|
133
|
+
lintCmd: string | null;
|
|
134
|
+
buildCmd: string | null;
|
|
135
|
+
typecheckCmd: string | null;
|
|
136
|
+
}
|
package/src/types/workflow.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { z } from 'zod';
|
|
|
7
7
|
import { OutputLanguageSchema } from './project.js';
|
|
8
8
|
import type { OutputLanguage, OpenAIModel } from './project.js';
|
|
9
9
|
import type { ConsensusIteration } from './consensus.js';
|
|
10
|
+
import type { TestPlanOutput } from './tester.js';
|
|
11
|
+
import { TestPlanOutputSchema, TestVerdictSchema } from './tester.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Workflow phases
|
|
@@ -68,6 +70,17 @@ export interface Task {
|
|
|
68
70
|
|
|
69
71
|
// App target (which app this task affects)
|
|
70
72
|
appTarget?: 'frontend' | 'backend' | 'unified';
|
|
73
|
+
|
|
74
|
+
// Tester (QA) tracking
|
|
75
|
+
qaTestPlanText?: string; // Approved test plan (markdown, for humans)
|
|
76
|
+
qaTestPlanParsed?: TestPlanOutput; // Parsed structured plan (for machine flow)
|
|
77
|
+
qaTestPlanScore?: number; // Consensus score (0-100)
|
|
78
|
+
qaTestPlanIterations?: number; // Iterations to reach consensus
|
|
79
|
+
qaTestPlanApproved?: boolean; // Whether consensus was reached
|
|
80
|
+
qaTestPlanDoc?: string; // Path to docs/qa/test-plans/...
|
|
81
|
+
qaVerdict?: 'PASS' | 'PASS_WITH_NOTES' | 'FAIL';
|
|
82
|
+
qaReviewNotes?: string; // Tester's review notes
|
|
83
|
+
qaReviewDoc?: string; // Path to docs/qa/test-runs/...
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
/**
|
|
@@ -107,6 +120,16 @@ export const TaskSchema = z.object({
|
|
|
107
120
|
backendConsensus: AppConsensusTrackingSchema.optional(),
|
|
108
121
|
unifiedConsensus: AppConsensusTrackingSchema.optional(),
|
|
109
122
|
appTarget: z.enum(['frontend', 'backend', 'unified']).optional(),
|
|
123
|
+
// Tester (QA) tracking
|
|
124
|
+
qaTestPlanText: z.string().optional(),
|
|
125
|
+
qaTestPlanParsed: TestPlanOutputSchema.optional(),
|
|
126
|
+
qaTestPlanScore: z.number().optional(),
|
|
127
|
+
qaTestPlanIterations: z.number().optional(),
|
|
128
|
+
qaTestPlanApproved: z.boolean().optional(),
|
|
129
|
+
qaTestPlanDoc: z.string().optional(),
|
|
130
|
+
qaVerdict: TestVerdictSchema.optional(),
|
|
131
|
+
qaReviewNotes: z.string().optional(),
|
|
132
|
+
qaReviewDoc: z.string().optional(),
|
|
110
133
|
});
|
|
111
134
|
|
|
112
135
|
/**
|
|
@@ -205,6 +228,8 @@ export interface ProjectState {
|
|
|
205
228
|
strategyError?: string;
|
|
206
229
|
/** Absolute paths to discovered source documentation files */
|
|
207
230
|
sourceDocPaths?: string[];
|
|
231
|
+
/** Whether QA Tester skill is active (default: true for new projects, undefined/false for existing) */
|
|
232
|
+
qaEnabled?: boolean;
|
|
208
233
|
}
|
|
209
234
|
|
|
210
235
|
/**
|
|
@@ -250,6 +275,7 @@ export const ProjectStateSchema = z.object({
|
|
|
250
275
|
websiteStrategy: z.string().optional(),
|
|
251
276
|
strategyError: z.string().optional(),
|
|
252
277
|
sourceDocPaths: z.array(z.string()).optional(),
|
|
278
|
+
qaEnabled: z.boolean().optional(),
|
|
253
279
|
});
|
|
254
280
|
|
|
255
281
|
/**
|
|
@@ -865,10 +865,10 @@ ${task.name}
|
|
|
865
865
|
## Description
|
|
866
866
|
${task.description || task.name}
|
|
867
867
|
|
|
868
|
-
${task.
|
|
868
|
+
${task.qaTestPlanText ? `## QA Test Plan\nImplement tests according to this approved test plan:\n${task.qaTestPlanText}\n` : ''}
|
|
869
869
|
|
|
870
870
|
Please implement this task completely. After implementing:
|
|
871
|
-
1.
|
|
871
|
+
1. Implement tests as specified in the QA Test Plan above
|
|
872
872
|
2. Ensure code follows best practices
|
|
873
873
|
3. Document any complex logic
|
|
874
874
|
`.trim();
|
package/src/workflow/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export * from './project-structure.js';
|
|
|
48
48
|
export * from './separation-guard.js';
|
|
49
49
|
export * from './seo-tests.js';
|
|
50
50
|
export * from './task-workflow.js';
|
|
51
|
+
export * from './tester.js';
|
|
51
52
|
export * from './milestone-workflow.js';
|
|
52
53
|
export * from './plan-storage.js';
|
|
53
54
|
export * from './workspace-manager.js';
|
|
@@ -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. **
|
|
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:
|
|
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
|
-
|
|
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
|
|