edsger 0.3.3 → 0.4.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.
- package/dist/phases/code-refine/analyzer.d.ts +17 -0
- package/dist/phases/code-refine/analyzer.js +43 -3
- package/dist/phases/code-refine/retry-handler.d.ts +17 -0
- package/dist/phases/code-refine/retry-handler.js +123 -0
- package/dist/phases/code-refine-verification/verifier.d.ts +23 -3
- package/dist/phases/code-refine-verification/verifier.js +136 -38
- package/dist/workflow-runner/executors/phase-executor.js +25 -4
- package/dist/workflow-runner/pipeline-runner.js +10 -10
- package/package.json +1 -1
|
@@ -9,6 +9,23 @@ export interface CodeRefineOptions {
|
|
|
9
9
|
mcpToken: string;
|
|
10
10
|
githubToken: string;
|
|
11
11
|
verbose?: boolean;
|
|
12
|
+
verificationFailureContext?: {
|
|
13
|
+
attempt: number;
|
|
14
|
+
suggestions: string[];
|
|
15
|
+
unresolvedCommentDetails: Array<{
|
|
16
|
+
commentId: number;
|
|
17
|
+
author: string;
|
|
18
|
+
file: string;
|
|
19
|
+
line: number | null;
|
|
20
|
+
body: string;
|
|
21
|
+
}>;
|
|
22
|
+
unresolvedReviewDetails?: Array<{
|
|
23
|
+
reviewId: number;
|
|
24
|
+
author: string;
|
|
25
|
+
state: string;
|
|
26
|
+
body: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
12
29
|
}
|
|
13
30
|
export interface CodeRefineResult {
|
|
14
31
|
featureId: string;
|
|
@@ -142,7 +142,7 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
142
142
|
pullLatestChanges(verbose);
|
|
143
143
|
// Create prompt for code refine
|
|
144
144
|
const systemPrompt = createSystemPrompt(config);
|
|
145
|
-
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo);
|
|
145
|
+
const refinePrompt = createCodeRefinePrompt(featureId, context, feedbacksInfo, options.verificationFailureContext);
|
|
146
146
|
let lastAssistantResponse = '';
|
|
147
147
|
let structuredRefineResult = null;
|
|
148
148
|
if (verbose) {
|
|
@@ -336,20 +336,60 @@ You MUST end your response with a JSON object containing the code refine results
|
|
|
336
336
|
|
|
337
337
|
Focus on systematic code refinement based on the provided PR review feedback.`;
|
|
338
338
|
}
|
|
339
|
-
function createCodeRefinePrompt(featureId, context, feedbacksInfo) {
|
|
339
|
+
function createCodeRefinePrompt(featureId, context, feedbacksInfo, verificationFailureContext) {
|
|
340
340
|
let contextInfo = formatContextForPrompt(context);
|
|
341
341
|
if (feedbacksInfo) {
|
|
342
342
|
contextInfo = contextInfo + '\n\n' + feedbacksInfo;
|
|
343
343
|
}
|
|
344
|
-
|
|
344
|
+
// Add verification failure context if this is a retry
|
|
345
|
+
let verificationSection = '';
|
|
346
|
+
if (verificationFailureContext) {
|
|
347
|
+
verificationSection = `
|
|
348
|
+
|
|
349
|
+
## ⚠️ PREVIOUS ATTEMPT FAILED VERIFICATION (Attempt ${verificationFailureContext.attempt})
|
|
350
|
+
|
|
351
|
+
Your previous code refine attempt did NOT successfully address all the review feedback.
|
|
352
|
+
The verification phase found the following unresolved issues:
|
|
353
|
+
|
|
354
|
+
### Verification Failure Summary
|
|
355
|
+
${verificationFailureContext.suggestions.map((s) => `- ${s}`).join('\n')}
|
|
345
356
|
|
|
357
|
+
### Unresolved Comments Still Needing Attention
|
|
358
|
+
${verificationFailureContext.unresolvedCommentDetails
|
|
359
|
+
.map((detail, idx) => `${idx + 1}. **${detail.file}${detail.line ? `:${detail.line}` : ''}** by @${detail.author}
|
|
360
|
+
${detail.body}`)
|
|
361
|
+
.join('\n\n')}
|
|
362
|
+
|
|
363
|
+
${verificationFailureContext.unresolvedReviewDetails &&
|
|
364
|
+
verificationFailureContext.unresolvedReviewDetails.length > 0
|
|
365
|
+
? `
|
|
366
|
+
### Unresolved Reviews Still Requesting Changes
|
|
367
|
+
${verificationFailureContext.unresolvedReviewDetails
|
|
368
|
+
.map((review, idx) => `${idx + 1}. Review by @${review.author} (${review.state})
|
|
369
|
+
${review.body || 'No review body provided'}`)
|
|
370
|
+
.join('\n\n')}
|
|
371
|
+
`
|
|
372
|
+
: ''}
|
|
373
|
+
|
|
374
|
+
**CRITICAL**: You MUST address all the above issues in this iteration. Pay special attention to:
|
|
375
|
+
1. Read each unresolved comment carefully and understand what change is being requested
|
|
376
|
+
2. Locate the exact file and line mentioned
|
|
377
|
+
3. Make the specific changes requested by the reviewer
|
|
378
|
+
4. Test your changes to ensure they work correctly
|
|
379
|
+
5. Do not skip or overlook any comments - address ALL of them
|
|
380
|
+
`;
|
|
381
|
+
}
|
|
382
|
+
return `Refine code based on PR review feedback for feature: ${featureId}
|
|
383
|
+
${verificationFailureContext ? '\n🔄 **This is a RETRY after verification failure**\n' : ''}
|
|
346
384
|
${contextInfo}
|
|
385
|
+
${verificationSection}
|
|
347
386
|
|
|
348
387
|
## Code Refine Instructions
|
|
349
388
|
|
|
350
389
|
Follow this systematic approach:
|
|
351
390
|
|
|
352
391
|
1. **Analyze All Feedback**: Study all review comments and change requests above:
|
|
392
|
+
${verificationFailureContext ? ' - **FOCUS ON THE UNRESOLVED COMMENTS listed in the verification failure section**' : ''}
|
|
353
393
|
- What specific concerns were raised?
|
|
354
394
|
- Which files and code sections need changes?
|
|
355
395
|
- What are the underlying issues?
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code refine retry handler for pipeline execution
|
|
3
|
+
* Handles verification failures and retries code refine with suggestions
|
|
4
|
+
*/
|
|
5
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
6
|
+
import { PipelinePhaseOptions, PipelineResult } from '../../types/pipeline.js';
|
|
7
|
+
export declare const MAX_REFINE_ATTEMPTS = 10;
|
|
8
|
+
export interface CodeRefineRetryOptions {
|
|
9
|
+
options: PipelinePhaseOptions;
|
|
10
|
+
config: EdsgerConfig;
|
|
11
|
+
results: PipelineResult[];
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Handle code refine verification failures with automatic retry
|
|
16
|
+
*/
|
|
17
|
+
export declare function handleCodeRefineWithRetry({ options, config, results, verbose, }: CodeRefineRetryOptions): Promise<PipelineResult>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code refine retry handler for pipeline execution
|
|
3
|
+
* Handles verification failures and retries code refine with suggestions
|
|
4
|
+
*/
|
|
5
|
+
import { logPhaseResult } from '../../utils/pipeline-logger.js';
|
|
6
|
+
import { runCodeRefinePhase, runCodeRefineVerificationPhase, } from '../../workflow-runner/executors/phase-executor.js';
|
|
7
|
+
import { updateFeatureStatusForPhase } from '../../api/features/index.js';
|
|
8
|
+
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
9
|
+
export const MAX_REFINE_ATTEMPTS = 10;
|
|
10
|
+
/**
|
|
11
|
+
* Handle code refine verification failures with automatic retry
|
|
12
|
+
*/
|
|
13
|
+
export async function handleCodeRefineWithRetry({ options, config, results, verbose, }) {
|
|
14
|
+
let verificationResult;
|
|
15
|
+
let refineAttempt = 0;
|
|
16
|
+
let verificationFailureContext;
|
|
17
|
+
do {
|
|
18
|
+
// 1. Run code refine phase (with verification context if this is a retry)
|
|
19
|
+
const enhancedOptions = verificationFailureContext
|
|
20
|
+
? { ...options, verificationFailureContext }
|
|
21
|
+
: options;
|
|
22
|
+
const codeRefineResult = await runCodeRefinePhase(enhancedOptions, config);
|
|
23
|
+
results.push(logPhaseResult(codeRefineResult, verbose));
|
|
24
|
+
// If code refine failed, stop immediately
|
|
25
|
+
if (codeRefineResult.status === 'error') {
|
|
26
|
+
if (verbose) {
|
|
27
|
+
console.log(`❌ Code refine failed: ${codeRefineResult.message}. Cannot proceed to verification.`);
|
|
28
|
+
}
|
|
29
|
+
return codeRefineResult;
|
|
30
|
+
}
|
|
31
|
+
// 2. Run code refine verification
|
|
32
|
+
verificationResult = await runCodeRefineVerificationPhase(options, config);
|
|
33
|
+
results.push(logPhaseResult(verificationResult, verbose));
|
|
34
|
+
// If verification passed, we're done
|
|
35
|
+
if (verificationResult.status === 'success') {
|
|
36
|
+
if (verbose) {
|
|
37
|
+
console.log(`✅ Code refine verification passed. All PR comments have been addressed.`);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
// Verification failed - check if we should retry
|
|
42
|
+
if (refineAttempt < MAX_REFINE_ATTEMPTS) {
|
|
43
|
+
refineAttempt++;
|
|
44
|
+
if (verbose) {
|
|
45
|
+
console.log(`⚠️ Code refine verification failed. Retrying... (Attempt ${refineAttempt}/${MAX_REFINE_ATTEMPTS})`);
|
|
46
|
+
}
|
|
47
|
+
// Extract verification failure details
|
|
48
|
+
const verificationData = verificationResult.data;
|
|
49
|
+
const unresolvedComments = verificationData?.unresolvedComments ||
|
|
50
|
+
verificationData?.unresolved_comments ||
|
|
51
|
+
0;
|
|
52
|
+
const suggestions = verificationData?.suggestions || [];
|
|
53
|
+
const unresolvedCommentDetails = verificationData?.unresolvedCommentDetails ||
|
|
54
|
+
verificationData?.unresolved_comment_details ||
|
|
55
|
+
[];
|
|
56
|
+
const unresolvedReviewDetails = verificationData?.unresolvedReviewDetails ||
|
|
57
|
+
verificationData?.unresolved_review_details;
|
|
58
|
+
// Prepare verification failure context for next code-refine attempt
|
|
59
|
+
verificationFailureContext = {
|
|
60
|
+
attempt: refineAttempt + 1, // Next attempt number
|
|
61
|
+
suggestions,
|
|
62
|
+
unresolvedCommentDetails,
|
|
63
|
+
unresolvedReviewDetails,
|
|
64
|
+
};
|
|
65
|
+
// Log audit event for verification failure with suggestions
|
|
66
|
+
await logFeaturePhaseEvent(options.mcpServerUrl, options.mcpToken, {
|
|
67
|
+
featureId: options.featureId,
|
|
68
|
+
eventType: 'phase_failed',
|
|
69
|
+
phase: 'code_refine_verification',
|
|
70
|
+
result: 'error',
|
|
71
|
+
metadata: {
|
|
72
|
+
attempt: refineAttempt,
|
|
73
|
+
unresolved_comments: unresolvedComments,
|
|
74
|
+
suggestions: suggestions,
|
|
75
|
+
unresolved_comment_details: unresolvedCommentDetails,
|
|
76
|
+
unresolved_review_details: unresolvedReviewDetails,
|
|
77
|
+
will_retry: true,
|
|
78
|
+
},
|
|
79
|
+
errorMessage: verificationResult.message,
|
|
80
|
+
}, verbose);
|
|
81
|
+
if (verbose && suggestions.length > 0) {
|
|
82
|
+
console.log(`\n💡 Suggestions for next iteration:`);
|
|
83
|
+
suggestions.forEach((suggestion) => {
|
|
84
|
+
console.log(` ${suggestion}`);
|
|
85
|
+
});
|
|
86
|
+
console.log('');
|
|
87
|
+
}
|
|
88
|
+
// Remove the failed verification result - we'll add the new one after retry
|
|
89
|
+
results.pop();
|
|
90
|
+
// Update status to indicate we're retrying code refine
|
|
91
|
+
await updateFeatureStatusForPhase({ mcpServerUrl: options.mcpServerUrl, mcpToken: options.mcpToken }, options.featureId, 'code-refine', verbose);
|
|
92
|
+
// Also remove the code refine result so it can be retried
|
|
93
|
+
results.pop();
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Max attempts reached
|
|
97
|
+
if (verbose) {
|
|
98
|
+
console.log(`⚠️ Maximum refine attempts (${MAX_REFINE_ATTEMPTS}) reached. Verification still failing.`);
|
|
99
|
+
}
|
|
100
|
+
// Log final failure with all details
|
|
101
|
+
const verificationData = verificationResult.data;
|
|
102
|
+
const suggestions = verificationData?.suggestions || [];
|
|
103
|
+
const unresolvedDetails = verificationData?.unresolvedCommentDetails ||
|
|
104
|
+
verificationData?.unresolved_comment_details ||
|
|
105
|
+
[];
|
|
106
|
+
await logFeaturePhaseEvent(options.mcpServerUrl, options.mcpToken, {
|
|
107
|
+
featureId: options.featureId,
|
|
108
|
+
eventType: 'phase_failed',
|
|
109
|
+
phase: 'code_refine_verification',
|
|
110
|
+
result: 'error',
|
|
111
|
+
metadata: {
|
|
112
|
+
attempt: refineAttempt,
|
|
113
|
+
max_attempts_reached: true,
|
|
114
|
+
suggestions: suggestions,
|
|
115
|
+
unresolved_comment_details: unresolvedDetails,
|
|
116
|
+
},
|
|
117
|
+
errorMessage: `Max attempts reached: ${verificationResult.message}`,
|
|
118
|
+
}, verbose);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
} while (refineAttempt <= MAX_REFINE_ATTEMPTS);
|
|
122
|
+
return verificationResult;
|
|
123
|
+
}
|
|
@@ -9,14 +9,34 @@ export interface CodeRefineVerificationOptions {
|
|
|
9
9
|
githubToken: string;
|
|
10
10
|
verbose?: boolean;
|
|
11
11
|
}
|
|
12
|
-
export interface
|
|
12
|
+
export interface CodeRefineVerificationData {
|
|
13
13
|
featureId: string;
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
totalReviews: number;
|
|
15
|
+
unresolvedReviews: number;
|
|
16
16
|
totalComments: number;
|
|
17
17
|
resolvedComments: number;
|
|
18
18
|
unresolvedComments: number;
|
|
19
19
|
commentsMarkedResolved?: number;
|
|
20
|
+
suggestions?: string[];
|
|
21
|
+
unresolvedReviewDetails?: Array<{
|
|
22
|
+
reviewId: number;
|
|
23
|
+
author: string;
|
|
24
|
+
state: string;
|
|
25
|
+
body: string | null;
|
|
26
|
+
submittedAt: string | null;
|
|
27
|
+
}>;
|
|
28
|
+
unresolvedCommentDetails?: Array<{
|
|
29
|
+
commentId: number;
|
|
30
|
+
author: string;
|
|
31
|
+
file: string;
|
|
32
|
+
line: number | null;
|
|
33
|
+
body: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export interface CodeRefineVerificationResult {
|
|
37
|
+
status: 'success' | 'error';
|
|
38
|
+
message: string;
|
|
39
|
+
data: CodeRefineVerificationData;
|
|
20
40
|
}
|
|
21
41
|
/**
|
|
22
42
|
* Verify and resolve PR review comments
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Octokit } from '@octokit/rest';
|
|
6
6
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
7
|
-
import { parsePullRequestUrl, fetchPRReviewComments, } from '../code-refine/context-fetcher.js';
|
|
7
|
+
import { parsePullRequestUrl, fetchPRReviews, fetchPRReviewComments, } from '../code-refine/context-fetcher.js';
|
|
8
8
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
9
9
|
/**
|
|
10
10
|
* Check if a review comment thread is resolved
|
|
@@ -107,36 +107,65 @@ export async function verifyAndResolveComments(options) {
|
|
|
107
107
|
const octokit = new Octokit({
|
|
108
108
|
auth: githubToken,
|
|
109
109
|
});
|
|
110
|
-
// Fetch all review comments
|
|
111
|
-
const reviewComments = await
|
|
112
|
-
|
|
110
|
+
// Fetch all reviews requesting changes and review comments
|
|
111
|
+
const [reviews, reviewComments] = await Promise.all([
|
|
112
|
+
fetchPRReviews(octokit, owner, repo, prNumber, verbose),
|
|
113
|
+
fetchPRReviewComments(octokit, owner, repo, prNumber, verbose),
|
|
114
|
+
]);
|
|
115
|
+
if (reviews.length === 0 && reviewComments.length === 0) {
|
|
113
116
|
if (verbose) {
|
|
114
|
-
logInfo('✅ No review comments found. Verification complete.');
|
|
117
|
+
logInfo('✅ No reviews or review comments found. Verification complete.');
|
|
115
118
|
}
|
|
116
119
|
return {
|
|
117
|
-
featureId,
|
|
118
120
|
status: 'success',
|
|
119
|
-
message: 'No review comments to verify',
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
message: 'No reviews or review comments to verify',
|
|
122
|
+
data: {
|
|
123
|
+
featureId,
|
|
124
|
+
totalReviews: 0,
|
|
125
|
+
unresolvedReviews: 0,
|
|
126
|
+
totalComments: 0,
|
|
127
|
+
resolvedComments: 0,
|
|
128
|
+
unresolvedComments: 0,
|
|
129
|
+
},
|
|
123
130
|
};
|
|
124
131
|
}
|
|
125
132
|
// Check which comments are resolved
|
|
126
133
|
if (verbose) {
|
|
127
|
-
|
|
134
|
+
if (reviews.length > 0) {
|
|
135
|
+
logInfo(`🔍 Checking ${reviews.length} reviews requesting changes...`);
|
|
136
|
+
}
|
|
137
|
+
if (reviewComments.length > 0) {
|
|
138
|
+
logInfo(`🔍 Checking ${reviewComments.length} review comments...`);
|
|
139
|
+
}
|
|
128
140
|
}
|
|
129
|
-
|
|
141
|
+
// Filter out already resolved comments (position is null but original_position exists)
|
|
142
|
+
// This is a heuristic - when code is modified at comment location, GitHub sets position to null
|
|
143
|
+
const unresolvedCommentsCandidates = reviewComments.filter((comment) =>
|
|
144
|
+
// Include comments where position is still set (not yet addressed)
|
|
145
|
+
// OR where both position and original_position are null (general comments)
|
|
146
|
+
comment.position !== null ||
|
|
147
|
+
(comment.position === null && comment.original_position === null));
|
|
148
|
+
if (verbose && unresolvedCommentsCandidates.length < reviewComments.length) {
|
|
149
|
+
logInfo(`📊 Filtered out ${reviewComments.length - unresolvedCommentsCandidates.length} likely resolved comments (position changed from original)`);
|
|
150
|
+
}
|
|
151
|
+
// Check review comments resolution status for the candidates
|
|
152
|
+
const commentStatus = await Promise.all(unresolvedCommentsCandidates.map(async (comment) => {
|
|
130
153
|
const isResolved = await isCommentThreadResolved(octokit, owner, repo, prNumber, comment.id, verbose);
|
|
131
154
|
return { comment, isResolved };
|
|
132
155
|
}));
|
|
133
156
|
const resolvedComments = commentStatus.filter((c) => c.isResolved);
|
|
134
157
|
const unresolvedComments = commentStatus.filter((c) => !c.isResolved);
|
|
158
|
+
// Check reviews - they need to be dismissed or re-reviewed
|
|
159
|
+
// We consider reviews as "unresolved" if they are still in CHANGES_REQUESTED state
|
|
160
|
+
const unresolvedReviews = reviews.filter((review) => review.state === 'CHANGES_REQUESTED');
|
|
135
161
|
if (verbose) {
|
|
136
|
-
logInfo(`📊
|
|
162
|
+
logInfo(`📊 Comments: ${resolvedComments.length} resolved, ${unresolvedComments.length} unresolved`);
|
|
163
|
+
if (reviews.length > 0) {
|
|
164
|
+
logInfo(`📊 Reviews: ${reviews.length - unresolvedReviews.length} addressed, ${unresolvedReviews.length} still requesting changes`);
|
|
165
|
+
}
|
|
137
166
|
}
|
|
138
|
-
// If all comments are resolved
|
|
139
|
-
if (unresolvedComments.length === 0) {
|
|
167
|
+
// If all comments are resolved AND no reviews requesting changes, success
|
|
168
|
+
if (unresolvedComments.length === 0 && unresolvedReviews.length === 0) {
|
|
140
169
|
if (verbose) {
|
|
141
170
|
logInfo('✅ All comments have been addressed! Marking them as resolved...');
|
|
142
171
|
}
|
|
@@ -150,34 +179,95 @@ export async function verifyAndResolveComments(options) {
|
|
|
150
179
|
if (verbose) {
|
|
151
180
|
logInfo(`✅ Marked ${markedCount} comments as resolved on GitHub`);
|
|
152
181
|
}
|
|
182
|
+
const successMessage = reviews.length > 0
|
|
183
|
+
? 'All reviews and review comments have been addressed and resolved'
|
|
184
|
+
: 'All review comments have been addressed and resolved';
|
|
153
185
|
return {
|
|
154
|
-
featureId,
|
|
155
186
|
status: 'success',
|
|
156
|
-
message:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
187
|
+
message: successMessage,
|
|
188
|
+
data: {
|
|
189
|
+
featureId,
|
|
190
|
+
totalReviews: reviews.length,
|
|
191
|
+
unresolvedReviews: 0,
|
|
192
|
+
totalComments: reviewComments.length,
|
|
193
|
+
resolvedComments: resolvedComments.length,
|
|
194
|
+
unresolvedComments: 0,
|
|
195
|
+
commentsMarkedResolved: markedCount,
|
|
196
|
+
},
|
|
161
197
|
};
|
|
162
198
|
}
|
|
163
199
|
else {
|
|
164
200
|
if (verbose) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
201
|
+
if (unresolvedReviews.length > 0) {
|
|
202
|
+
logInfo(`⚠️ ${unresolvedReviews.length} reviews still requesting changes`);
|
|
203
|
+
unresolvedReviews.forEach((review) => {
|
|
204
|
+
logInfo(` - Review ${review.id} by @${review.user.login}`);
|
|
205
|
+
if (review.body) {
|
|
206
|
+
logInfo(` ${review.body.substring(0, 100)}...`);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (unresolvedComments.length > 0) {
|
|
211
|
+
logInfo(`⚠️ ${unresolvedComments.length} comments still need to be addressed`);
|
|
212
|
+
unresolvedComments.forEach(({ comment }) => {
|
|
213
|
+
logInfo(` - Comment ${comment.id} by @${comment.user.login}`);
|
|
214
|
+
if (comment.path) {
|
|
215
|
+
logInfo(` File: ${comment.path}:${comment.line || '?'}`);
|
|
216
|
+
}
|
|
217
|
+
logInfo(` ${comment.body.substring(0, 100)}...`);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Build concise suggestions for code-refine phase (similar to technical-design)
|
|
222
|
+
const suggestions = [];
|
|
223
|
+
suggestions.push('Verification failed - address the following issues:');
|
|
224
|
+
if (unresolvedReviews.length > 0) {
|
|
225
|
+
suggestions.push(`1. ${unresolvedReviews.length} review(s) still requesting changes - address the overall concerns raised by reviewers`);
|
|
226
|
+
}
|
|
227
|
+
if (unresolvedComments.length > 0) {
|
|
228
|
+
suggestions.push(`${unresolvedReviews.length > 0 ? '2' : '1'}. ${unresolvedComments.length} unresolved comment(s) - carefully address each specific feedback point`);
|
|
229
|
+
}
|
|
230
|
+
suggestions.push(`${unresolvedReviews.length > 0 || unresolvedComments.length > 0 ? (unresolvedReviews.length > 0 && unresolvedComments.length > 0 ? '3' : '2') : '2'}. Review the unresolved_comment_details in metadata for specific locations and feedback`);
|
|
231
|
+
suggestions.push(`${unresolvedReviews.length > 0 || unresolvedComments.length > 0 ? (unresolvedReviews.length > 0 && unresolvedComments.length > 0 ? '4' : '3') : '3'}. Test your changes thoroughly before committing`);
|
|
232
|
+
// Build detailed unresolved info
|
|
233
|
+
const unresolvedCommentDetails = unresolvedComments.map(({ comment }) => ({
|
|
234
|
+
commentId: comment.id,
|
|
235
|
+
author: comment.user.login,
|
|
236
|
+
file: comment.path || 'unknown',
|
|
237
|
+
line: comment.line,
|
|
238
|
+
body: comment.body,
|
|
239
|
+
}));
|
|
240
|
+
const unresolvedReviewDetails = unresolvedReviews.map((review) => ({
|
|
241
|
+
reviewId: review.id,
|
|
242
|
+
author: review.user.login,
|
|
243
|
+
state: review.state,
|
|
244
|
+
body: review.body,
|
|
245
|
+
submittedAt: review.submitted_at,
|
|
246
|
+
}));
|
|
247
|
+
let errorMessage = '';
|
|
248
|
+
if (unresolvedReviews.length > 0 && unresolvedComments.length > 0) {
|
|
249
|
+
errorMessage = `${unresolvedReviews.length} reviews and ${unresolvedComments.length} comments still need to be addressed`;
|
|
250
|
+
}
|
|
251
|
+
else if (unresolvedReviews.length > 0) {
|
|
252
|
+
errorMessage = `${unresolvedReviews.length} reviews still requesting changes`;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
errorMessage = `${unresolvedComments.length} review comments still need to be addressed`;
|
|
173
256
|
}
|
|
174
257
|
return {
|
|
175
|
-
featureId,
|
|
176
258
|
status: 'error',
|
|
177
|
-
message:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
259
|
+
message: errorMessage,
|
|
260
|
+
data: {
|
|
261
|
+
featureId,
|
|
262
|
+
totalReviews: reviews.length,
|
|
263
|
+
unresolvedReviews: unresolvedReviews.length,
|
|
264
|
+
totalComments: reviewComments.length,
|
|
265
|
+
resolvedComments: resolvedComments.length,
|
|
266
|
+
unresolvedComments: unresolvedComments.length,
|
|
267
|
+
suggestions,
|
|
268
|
+
unresolvedReviewDetails,
|
|
269
|
+
unresolvedCommentDetails,
|
|
270
|
+
},
|
|
181
271
|
};
|
|
182
272
|
}
|
|
183
273
|
}
|
|
@@ -185,12 +275,20 @@ export async function verifyAndResolveComments(options) {
|
|
|
185
275
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
186
276
|
logError(`Code refine verification failed: ${errorMessage}`);
|
|
187
277
|
return {
|
|
188
|
-
featureId,
|
|
189
278
|
status: 'error',
|
|
190
279
|
message: `Verification failed: ${errorMessage}`,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
280
|
+
data: {
|
|
281
|
+
featureId,
|
|
282
|
+
totalReviews: 0,
|
|
283
|
+
unresolvedReviews: 0,
|
|
284
|
+
totalComments: 0,
|
|
285
|
+
resolvedComments: 0,
|
|
286
|
+
unresolvedComments: 0,
|
|
287
|
+
suggestions: [
|
|
288
|
+
`Verification failed with error: ${errorMessage}`,
|
|
289
|
+
'Please check the error message and try again',
|
|
290
|
+
],
|
|
291
|
+
},
|
|
194
292
|
};
|
|
195
293
|
}
|
|
196
294
|
}
|
|
@@ -173,15 +173,36 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
173
173
|
}, verbose);
|
|
174
174
|
}
|
|
175
175
|
else {
|
|
176
|
+
// Extract additional failure details from result data
|
|
177
|
+
const failureMetadata = {
|
|
178
|
+
duration_ms: phaseDuration,
|
|
179
|
+
};
|
|
180
|
+
// For verification phases, include suggestions and details
|
|
181
|
+
if (name.includes('verification') &&
|
|
182
|
+
result.data &&
|
|
183
|
+
typeof result.data === 'object') {
|
|
184
|
+
const data = result.data;
|
|
185
|
+
if (data.suggestions) {
|
|
186
|
+
failureMetadata.suggestions = data.suggestions;
|
|
187
|
+
}
|
|
188
|
+
if (data.unresolvedCommentDetails ||
|
|
189
|
+
data.unresolved_comment_details) {
|
|
190
|
+
failureMetadata.unresolved_comment_details =
|
|
191
|
+
data.unresolvedCommentDetails || data.unresolved_comment_details;
|
|
192
|
+
}
|
|
193
|
+
if (data.unresolvedComments !== undefined ||
|
|
194
|
+
data.unresolved_comments !== undefined) {
|
|
195
|
+
failureMetadata.unresolved_comments =
|
|
196
|
+
data.unresolvedComments ?? data.unresolved_comments;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
176
199
|
await logFeaturePhaseEvent(mcpServerUrl, mcpToken, {
|
|
177
200
|
featureId,
|
|
178
201
|
eventType: 'phase_failed',
|
|
179
202
|
phase: name.replace(/-/g, '_'),
|
|
180
203
|
result: 'error',
|
|
181
|
-
metadata:
|
|
182
|
-
|
|
183
|
-
},
|
|
184
|
-
errorMessage: result.summary || 'Phase execution failed',
|
|
204
|
+
metadata: failureMetadata,
|
|
205
|
+
errorMessage: result.summary || result.message || 'Phase execution failed',
|
|
185
206
|
}, verbose);
|
|
186
207
|
}
|
|
187
208
|
return {
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Uses functional programming principles
|
|
5
5
|
*/
|
|
6
6
|
import { updateFeatureStatusForPhase } from '../api/features/index.js';
|
|
7
|
-
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase,
|
|
7
|
+
import { runFeatureAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, } from './executors/phase-executor.js';
|
|
8
8
|
import { logPipelineStart, logPhaseResult, logPipelineComplete, shouldContinuePipeline, } from '../utils/pipeline-logger.js';
|
|
9
9
|
import { handleTestFailuresWithRetry } from '../phases/functional-testing/test-retry-handler.js';
|
|
10
|
+
import { handleCodeRefineWithRetry } from '../phases/code-refine/retry-handler.js';
|
|
10
11
|
import { handlePullRequestCreation } from '../phases/pull-request/handler.js';
|
|
11
12
|
/**
|
|
12
13
|
* Run pipeline based on execution mode
|
|
@@ -267,19 +268,18 @@ const runFromFunctionalTesting = async (options, config) => {
|
|
|
267
268
|
};
|
|
268
269
|
/**
|
|
269
270
|
* Run only code refine phase (refine code based on PR feedback)
|
|
271
|
+
* Includes automatic retry loop for verification failures
|
|
270
272
|
*/
|
|
271
273
|
const runOnlyCodeRefine = async (options, config) => {
|
|
272
274
|
const { featureId, verbose } = options;
|
|
273
275
|
logPipelineStart(featureId, verbose);
|
|
274
276
|
const results = [];
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const verificationResult = await runCodeRefineVerificationPhase(options, config);
|
|
283
|
-
results.push(logPhaseResult(verificationResult, verbose));
|
|
277
|
+
// Code Refine with automatic retry for verification failures
|
|
278
|
+
await handleCodeRefineWithRetry({
|
|
279
|
+
options,
|
|
280
|
+
config,
|
|
281
|
+
results,
|
|
282
|
+
verbose,
|
|
283
|
+
});
|
|
284
284
|
return logPipelineComplete(results, verbose);
|
|
285
285
|
};
|