edsger 0.19.10 → 0.19.11
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/index.js +17 -8
- package/dist/phases/code-review/index.js +22 -1
- package/dist/services/branches.d.ts +6 -1
- package/dist/services/branches.js +28 -9
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -28
- package/dist/api/features/__tests__/regression-prevention.test.d.ts +0 -5
- package/dist/api/features/__tests__/regression-prevention.test.js +0 -338
- package/dist/api/features/__tests__/status-updater.integration.test.d.ts +0 -5
- package/dist/api/features/__tests__/status-updater.integration.test.js +0 -497
- package/dist/commands/workflow/pipeline-runner.d.ts +0 -17
- package/dist/commands/workflow/pipeline-runner.js +0 -393
- package/dist/commands/workflow/runner.d.ts +0 -26
- package/dist/commands/workflow/runner.js +0 -119
- package/dist/commands/workflow/workflow-runner.d.ts +0 -26
- package/dist/commands/workflow/workflow-runner.js +0 -119
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +0 -28
- package/dist/phases/code-implementation/analyzer-helpers.js +0 -177
- package/dist/phases/code-implementation/analyzer.d.ts +0 -32
- package/dist/phases/code-implementation/analyzer.js +0 -629
- package/dist/phases/code-implementation/context-fetcher.d.ts +0 -17
- package/dist/phases/code-implementation/context-fetcher.js +0 -86
- package/dist/phases/code-implementation/mcp-server.d.ts +0 -1
- package/dist/phases/code-implementation/mcp-server.js +0 -93
- package/dist/phases/code-implementation/prompts-improvement.d.ts +0 -5
- package/dist/phases/code-implementation/prompts-improvement.js +0 -108
- package/dist/phases/code-implementation-verification/verifier.d.ts +0 -31
- package/dist/phases/code-implementation-verification/verifier.js +0 -196
- package/dist/phases/code-refine/analyzer.d.ts +0 -41
- package/dist/phases/code-refine/analyzer.js +0 -561
- package/dist/phases/code-refine/context-fetcher.d.ts +0 -94
- package/dist/phases/code-refine/context-fetcher.js +0 -423
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +0 -22
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +0 -134
- package/dist/phases/code-refine-verification/verifier.d.ts +0 -47
- package/dist/phases/code-refine-verification/verifier.js +0 -597
- package/dist/phases/code-review/analyzer.d.ts +0 -29
- package/dist/phases/code-review/analyzer.js +0 -363
- package/dist/phases/code-review/context-fetcher.d.ts +0 -92
- package/dist/phases/code-review/context-fetcher.js +0 -296
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +0 -10
- package/dist/phases/feature-analysis/analyzer-helpers.js +0 -47
- package/dist/phases/feature-analysis/analyzer.d.ts +0 -11
- package/dist/phases/feature-analysis/analyzer.js +0 -208
- package/dist/phases/feature-analysis/context-fetcher.d.ts +0 -26
- package/dist/phases/feature-analysis/context-fetcher.js +0 -134
- package/dist/phases/feature-analysis/http-fallback.d.ts +0 -20
- package/dist/phases/feature-analysis/http-fallback.js +0 -95
- package/dist/phases/feature-analysis/mcp-server.d.ts +0 -1
- package/dist/phases/feature-analysis/mcp-server.js +0 -144
- package/dist/phases/feature-analysis/prompts-improvement.d.ts +0 -8
- package/dist/phases/feature-analysis/prompts-improvement.js +0 -109
- package/dist/phases/feature-analysis-verification/verifier.d.ts +0 -37
- package/dist/phases/feature-analysis-verification/verifier.js +0 -147
- package/dist/phases/technical-design/analyzer-helpers.d.ts +0 -25
- package/dist/phases/technical-design/analyzer-helpers.js +0 -39
- package/dist/phases/technical-design/analyzer.d.ts +0 -21
- package/dist/phases/technical-design/analyzer.js +0 -461
- package/dist/phases/technical-design/context-fetcher.d.ts +0 -12
- package/dist/phases/technical-design/context-fetcher.js +0 -39
- package/dist/phases/technical-design/http-fallback.d.ts +0 -17
- package/dist/phases/technical-design/http-fallback.js +0 -151
- package/dist/phases/technical-design/mcp-server.d.ts +0 -1
- package/dist/phases/technical-design/mcp-server.js +0 -157
- package/dist/phases/technical-design/prompts-improvement.d.ts +0 -5
- package/dist/phases/technical-design/prompts-improvement.js +0 -93
- package/dist/phases/technical-design-verification/verifier.d.ts +0 -53
- package/dist/phases/technical-design-verification/verifier.js +0 -170
- package/dist/services/feature-branches.d.ts +0 -77
- package/dist/services/feature-branches.js +0 -205
- package/dist/workflow-runner/config/phase-configs.d.ts +0 -5
- package/dist/workflow-runner/config/phase-configs.js +0 -120
- package/dist/workflow-runner/core/feature-filter.d.ts +0 -16
- package/dist/workflow-runner/core/feature-filter.js +0 -46
- package/dist/workflow-runner/core/index.d.ts +0 -8
- package/dist/workflow-runner/core/index.js +0 -12
- package/dist/workflow-runner/core/pipeline-evaluator.d.ts +0 -24
- package/dist/workflow-runner/core/pipeline-evaluator.js +0 -32
- package/dist/workflow-runner/core/state-manager.d.ts +0 -24
- package/dist/workflow-runner/core/state-manager.js +0 -42
- package/dist/workflow-runner/core/workflow-logger.d.ts +0 -20
- package/dist/workflow-runner/core/workflow-logger.js +0 -65
- package/dist/workflow-runner/executors/phase-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/phase-executor.js +0 -248
- package/dist/workflow-runner/feature-workflow-runner.d.ts +0 -26
- package/dist/workflow-runner/feature-workflow-runner.js +0 -119
- package/dist/workflow-runner/index.d.ts +0 -2
- package/dist/workflow-runner/index.js +0 -2
- package/dist/workflow-runner/pipeline-runner.d.ts +0 -17
- package/dist/workflow-runner/pipeline-runner.js +0 -393
- package/dist/workflow-runner/workflow-processor.d.ts +0 -54
- package/dist/workflow-runner/workflow-processor.js +0 -170
|
@@ -11,7 +11,7 @@ import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services
|
|
|
11
11
|
import { createSystemPrompt, createCodeRefinePrompt } from './prompts.js';
|
|
12
12
|
import { preparePhaseGitEnvironment, prepareCustomBranchGitEnvironment, hasUncommittedChanges, getUncommittedFiles, syncFeatBranchWithMain, } from '../../utils/git-branch-manager.js';
|
|
13
13
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
14
|
-
import {
|
|
14
|
+
import { getReviewedBranch, updateBranch, } from '../../services/branches.js';
|
|
15
15
|
import { parsePullRequestUrl } from './context.js';
|
|
16
16
|
import { verifyAndResolveComments, } from '../code-refine-verification/index.js';
|
|
17
17
|
// Maximum number of refine + verification iterations
|
|
@@ -71,22 +71,22 @@ export const refineCodeFromPRFeedback = async (options, config) => {
|
|
|
71
71
|
if (verbose) {
|
|
72
72
|
logInfo(`Starting code refine for feature ID: ${featureId}`);
|
|
73
73
|
}
|
|
74
|
-
// For multi-branch features, find the branch that
|
|
75
|
-
// and use its branch_name for refine
|
|
74
|
+
// For multi-branch features, find the branch that has been reviewed
|
|
75
|
+
// (status = 'reviewed' after code-review phase) and use its branch_name for refine
|
|
76
76
|
let branchName = `dev/${featureId}`; // Default for single-branch features
|
|
77
77
|
let currentBranch = null;
|
|
78
78
|
try {
|
|
79
|
-
currentBranch = await
|
|
79
|
+
currentBranch = await getReviewedBranch({ featureId, verbose });
|
|
80
80
|
if (currentBranch && currentBranch.branch_name) {
|
|
81
81
|
// Use branch_name directly (already stored as dev/...)
|
|
82
82
|
branchName = currentBranch.branch_name;
|
|
83
83
|
if (verbose) {
|
|
84
|
-
logInfo(`📋 Found
|
|
84
|
+
logInfo(`📋 Found reviewed branch: ${currentBranch.name}`);
|
|
85
85
|
logInfo(` Using dev branch: ${branchName}`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
else if (verbose) {
|
|
89
|
-
logInfo(`ℹ️ No
|
|
89
|
+
logInfo(`ℹ️ No reviewed branch found, using default: ${branchName}`);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
catch (error) {
|
|
@@ -232,11 +232,19 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
232
232
|
if (verbose) {
|
|
233
233
|
logInfo('✅ Verification passed! All PR comments have been addressed and resolved.');
|
|
234
234
|
}
|
|
235
|
+
// Update branch status to 'refined' for multi-branch features
|
|
236
|
+
if (currentBranch) {
|
|
237
|
+
await updateBranch(currentBranch.id, { status: 'refined' }, verbose);
|
|
238
|
+
if (verbose) {
|
|
239
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'refined'`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
235
242
|
return {
|
|
236
243
|
featureId,
|
|
237
244
|
status: 'success',
|
|
238
245
|
message: verificationResult.message,
|
|
239
|
-
summary: lastRefineResult.summary ||
|
|
246
|
+
summary: lastRefineResult.summary ||
|
|
247
|
+
'Code refined based on PR review feedback',
|
|
240
248
|
filesModified: lastRefineResult.files_modified || [],
|
|
241
249
|
commitsCreated: lastRefineResult.commits_created || 1,
|
|
242
250
|
iterations: currentIteration,
|
|
@@ -277,7 +285,8 @@ Please ensure Claude Code commits all changes before completing the refine phase
|
|
|
277
285
|
return {
|
|
278
286
|
featureId,
|
|
279
287
|
status: 'error',
|
|
280
|
-
message: lastVerificationResult?.message ||
|
|
288
|
+
message: lastVerificationResult?.message ||
|
|
289
|
+
'Verification failed after max iterations',
|
|
281
290
|
summary: lastRefineResult?.summary,
|
|
282
291
|
filesModified: lastRefineResult?.files_modified || [],
|
|
283
292
|
commitsCreated: lastRefineResult?.commits_created || 1,
|
|
@@ -8,7 +8,7 @@ import { Octokit } from '@octokit/rest';
|
|
|
8
8
|
import { fetchCodeReviewContext, formatContextForPrompt, } from './context.js';
|
|
9
9
|
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
10
10
|
import { preparePhaseGitEnvironment, prepareCustomBranchGitEnvironment, } from '../../utils/git-branch-manager.js';
|
|
11
|
-
import { getReadyForReviewBranch, } from '../../services/branches.js';
|
|
11
|
+
import { getReadyForReviewBranch, updateBranch, } from '../../services/branches.js';
|
|
12
12
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
13
13
|
function userMessage(content) {
|
|
14
14
|
return {
|
|
@@ -319,6 +319,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
319
319
|
overall_assessment ||
|
|
320
320
|
'Code review completed. No issues found.',
|
|
321
321
|
});
|
|
322
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
323
|
+
if (currentBranch) {
|
|
324
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
325
|
+
if (verbose) {
|
|
326
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
322
329
|
return {
|
|
323
330
|
featureId,
|
|
324
331
|
status: 'success',
|
|
@@ -389,6 +396,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
389
396
|
body: (summary || overall_assessment || 'Code review completed.') +
|
|
390
397
|
'\n\n**Note**: Some review comments could not be posted because they referenced lines not present in the diff.',
|
|
391
398
|
});
|
|
399
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
400
|
+
if (currentBranch) {
|
|
401
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
402
|
+
if (verbose) {
|
|
403
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
392
406
|
return {
|
|
393
407
|
featureId,
|
|
394
408
|
status: 'success',
|
|
@@ -418,6 +432,13 @@ export const reviewPullRequest = async (options, config) => {
|
|
|
418
432
|
logInfo(`Review ID: ${review.data.id}`);
|
|
419
433
|
logInfo(`Comments posted: ${comments.length}`);
|
|
420
434
|
}
|
|
435
|
+
// Update branch status to 'reviewed' for multi-branch features
|
|
436
|
+
if (currentBranch) {
|
|
437
|
+
await updateBranch(currentBranch.id, { status: 'reviewed' }, verbose);
|
|
438
|
+
if (verbose) {
|
|
439
|
+
logInfo(`✅ Branch "${currentBranch.name}" status updated to 'reviewed'`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
421
442
|
return {
|
|
422
443
|
featureId,
|
|
423
444
|
status: 'success',
|
|
@@ -12,7 +12,7 @@ export interface Branch {
|
|
|
12
12
|
base_branch_id: string | null;
|
|
13
13
|
pull_request_url: string | null;
|
|
14
14
|
pull_request_number: number | null;
|
|
15
|
-
status: 'pending' | 'in_progress' | 'ready_for_review' | 'merged' | 'closed';
|
|
15
|
+
status: 'pending' | 'in_progress' | 'ready_for_review' | 'reviewed' | 'refined' | 'merged' | 'closed';
|
|
16
16
|
created_at: string;
|
|
17
17
|
updated_at: string;
|
|
18
18
|
}
|
|
@@ -80,6 +80,11 @@ export declare function getBranchById(branchId: string, verbose?: boolean): Prom
|
|
|
80
80
|
* Returns the first branch with status 'ready_for_review'
|
|
81
81
|
*/
|
|
82
82
|
export declare function getReadyForReviewBranch(options: PipelinePhaseOptions): Promise<Branch | null>;
|
|
83
|
+
/**
|
|
84
|
+
* Get the branch that has been reviewed (for code-refine phase)
|
|
85
|
+
* Returns the first branch with status 'reviewed'
|
|
86
|
+
*/
|
|
87
|
+
export declare function getReviewedBranch(options: PipelinePhaseOptions): Promise<Branch | null>;
|
|
83
88
|
/**
|
|
84
89
|
* Get the branch that is in progress (in_progress or ready_for_review)
|
|
85
90
|
* Useful for phases that need to work on the current active branch
|
|
@@ -95,17 +95,28 @@ export function formatBranchesForContext(branches) {
|
|
|
95
95
|
if (!branches || branches.length === 0) {
|
|
96
96
|
return 'No branches defined yet.';
|
|
97
97
|
}
|
|
98
|
+
const getStatusEmoji = (status) => {
|
|
99
|
+
switch (status) {
|
|
100
|
+
case 'merged':
|
|
101
|
+
return '✅';
|
|
102
|
+
case 'refined':
|
|
103
|
+
return '🎯';
|
|
104
|
+
case 'reviewed':
|
|
105
|
+
return '📝';
|
|
106
|
+
case 'ready_for_review':
|
|
107
|
+
return '👀';
|
|
108
|
+
case 'in_progress':
|
|
109
|
+
return '🔄';
|
|
110
|
+
case 'closed':
|
|
111
|
+
return '❌';
|
|
112
|
+
case 'pending':
|
|
113
|
+
default:
|
|
114
|
+
return '⏳';
|
|
115
|
+
}
|
|
116
|
+
};
|
|
98
117
|
const branchList = branches
|
|
99
118
|
.map((b, idx) => {
|
|
100
|
-
const statusEmoji = b.status
|
|
101
|
-
? '✅'
|
|
102
|
-
: b.status === 'in_progress'
|
|
103
|
-
? '🔄'
|
|
104
|
-
: b.status === 'ready_for_review'
|
|
105
|
-
? '👀'
|
|
106
|
-
: b.status === 'closed'
|
|
107
|
-
? '❌'
|
|
108
|
-
: '⏳';
|
|
119
|
+
const statusEmoji = getStatusEmoji(b.status);
|
|
109
120
|
return `${idx + 1}. **${b.name}** ${statusEmoji}
|
|
110
121
|
- Status: ${b.status}
|
|
111
122
|
- Branch: ${b.branch_name || 'Not created'}
|
|
@@ -211,6 +222,14 @@ export async function getReadyForReviewBranch(options) {
|
|
|
211
222
|
const branches = await getBranches(options);
|
|
212
223
|
return branches.find((b) => b.status === 'ready_for_review') || null;
|
|
213
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the branch that has been reviewed (for code-refine phase)
|
|
227
|
+
* Returns the first branch with status 'reviewed'
|
|
228
|
+
*/
|
|
229
|
+
export async function getReviewedBranch(options) {
|
|
230
|
+
const branches = await getBranches(options);
|
|
231
|
+
return branches.find((b) => b.status === 'reviewed') || null;
|
|
232
|
+
}
|
|
214
233
|
/**
|
|
215
234
|
* Get the branch that is in progress (in_progress or ready_for_review)
|
|
216
235
|
* Useful for phases that need to work on the current active branch
|
package/package.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Read(//Users/steven/development/edsger/**)",
|
|
5
|
-
"Bash(npm run build)",
|
|
6
|
-
"Bash(node:*)",
|
|
7
|
-
"Bash(git add:*)",
|
|
8
|
-
"Bash(git commit:*)",
|
|
9
|
-
"Bash(ls:*)",
|
|
10
|
-
"Bash(cat:*)",
|
|
11
|
-
"Bash(npm run typecheck:*)",
|
|
12
|
-
"Bash(git diff:*)",
|
|
13
|
-
"WebSearch",
|
|
14
|
-
"WebFetch(domain:supabase.com)",
|
|
15
|
-
"Bash(npm install:*)",
|
|
16
|
-
"Bash(grep:*)",
|
|
17
|
-
"Bash(npx supabase gen types typescript --help:*)",
|
|
18
|
-
"Bash(git -C /Users/steven/development/edsger status)",
|
|
19
|
-
"Bash(git -C /Users/steven/development/edsger diff)",
|
|
20
|
-
"Bash(git -C /Users/steven/development/edsger log --oneline -5)",
|
|
21
|
-
"Bash(git -C /Users/steven/development/edsger add supabase/migrations/20251231000000_drop_unused_views.sql)",
|
|
22
|
-
"Bash(git -C /Users/steven/development/edsger commit -m \"$\\(cat <<''EOF''\nchore: drop unused database views\n\nRemove test_report_summary and user_stories_with_context views that are defined but never used in the application.\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
|
|
23
|
-
"Bash(git -C /Users/steven/development/edsger commit -m \"$\\(cat <<''EOF''\nchore: drop unused database views\n\nRemove test_report_summary and user_stories_with_context views\nthat are defined but never used in the application.\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
|
|
24
|
-
],
|
|
25
|
-
"deny": [],
|
|
26
|
-
"ask": []
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Specialized tests for status regression prevention
|
|
3
|
-
* Focuses on the core business requirement: preventing backward status movement
|
|
4
|
-
*/
|
|
5
|
-
import { describe, it } from 'node:test';
|
|
6
|
-
import assert from 'node:assert';
|
|
7
|
-
import { isForwardProgression } from '../status-updater.js';
|
|
8
|
-
import { STATUS_PROGRESSION_ORDER } from '../../../config/feature-status.js';
|
|
9
|
-
describe('Regression Prevention Tests', () => {
|
|
10
|
-
describe('Core Business Logic: Prevent Backlog Regression', () => {
|
|
11
|
-
it('should prevent any status from regressing to backlog', () => {
|
|
12
|
-
const allStatuses = STATUS_PROGRESSION_ORDER.slice(1); // All except backlog itself
|
|
13
|
-
for (const status of allStatuses) {
|
|
14
|
-
const result = isForwardProgression(status, 'backlog');
|
|
15
|
-
assert.strictEqual(result, false, `Status ${status} should NOT be allowed to regress to backlog`);
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
it('should allow progression from backlog to any other status', () => {
|
|
19
|
-
const targetStatuses = STATUS_PROGRESSION_ORDER.slice(1); // All except backlog
|
|
20
|
-
for (const targetStatus of targetStatuses) {
|
|
21
|
-
const result = isForwardProgression('backlog', targetStatus);
|
|
22
|
-
assert.strictEqual(result, true, `Should allow progression from backlog to ${targetStatus}`);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
it('should prevent regression from any status to ready_for_dev', () => {
|
|
26
|
-
// Only backlog should be allowed to progress to ready_for_dev
|
|
27
|
-
const higherStatuses = STATUS_PROGRESSION_ORDER.slice(2); // All except backlog and ready_for_dev
|
|
28
|
-
for (const status of higherStatuses) {
|
|
29
|
-
const result = isForwardProgression(status, 'ready_for_dev');
|
|
30
|
-
assert.strictEqual(result, false, `Status ${status} should NOT regress to ready_for_dev`);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
it('should prevent all forms of backward progression in critical workflow', () => {
|
|
34
|
-
// Test critical workflow progression points
|
|
35
|
-
const criticalProgression = [
|
|
36
|
-
'backlog',
|
|
37
|
-
'ready_for_dev',
|
|
38
|
-
'feature_analysis',
|
|
39
|
-
'technical_design',
|
|
40
|
-
'code_implementation',
|
|
41
|
-
'functional_testing',
|
|
42
|
-
'deployment',
|
|
43
|
-
'shipped',
|
|
44
|
-
];
|
|
45
|
-
// Test every possible backward combination
|
|
46
|
-
for (let i = 0; i < criticalProgression.length; i++) {
|
|
47
|
-
for (let j = 0; j < i; j++) {
|
|
48
|
-
const currentStatus = criticalProgression[i];
|
|
49
|
-
const targetStatus = criticalProgression[j];
|
|
50
|
-
const result = isForwardProgression(currentStatus, targetStatus);
|
|
51
|
-
assert.strictEqual(result, false, `Should prevent regression from ${currentStatus} to ${targetStatus}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe('CLI Phase Protection', () => {
|
|
57
|
-
it('should identify scenarios where CLI phases could cause regression', () => {
|
|
58
|
-
// Simulate CLI phases that could potentially cause issues
|
|
59
|
-
const problematicScenarios = [
|
|
60
|
-
{
|
|
61
|
-
currentStatus: 'code_implementation',
|
|
62
|
-
phase: 'feature-analysis',
|
|
63
|
-
expectedTargetStatus: 'feature_analysis',
|
|
64
|
-
description: 'CLI re-running feature analysis after code implementation',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
currentStatus: 'deployment',
|
|
68
|
-
phase: 'technical-design',
|
|
69
|
-
expectedTargetStatus: 'technical_design',
|
|
70
|
-
description: 'CLI running technical design after deployment',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
currentStatus: 'shipped',
|
|
74
|
-
phase: 'code-implementation',
|
|
75
|
-
expectedTargetStatus: 'code_implementation',
|
|
76
|
-
description: 'CLI attempting code implementation on shipped feature',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
currentStatus: 'testing_passed',
|
|
80
|
-
phase: 'feature-analysis',
|
|
81
|
-
expectedTargetStatus: 'feature_analysis',
|
|
82
|
-
description: 'CLI re-analyzing feature after tests passed',
|
|
83
|
-
},
|
|
84
|
-
];
|
|
85
|
-
for (const scenario of problematicScenarios) {
|
|
86
|
-
const result = isForwardProgression(scenario.currentStatus, scenario.expectedTargetStatus);
|
|
87
|
-
assert.strictEqual(result, false, `Regression prevention should block: ${scenario.description}`);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
it('should allow valid CLI phase progressions', () => {
|
|
91
|
-
const validScenarios = [
|
|
92
|
-
{
|
|
93
|
-
currentStatus: 'backlog',
|
|
94
|
-
phase: 'feature-analysis',
|
|
95
|
-
expectedTargetStatus: 'feature_analysis',
|
|
96
|
-
description: 'CLI starting feature analysis from backlog',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
currentStatus: 'feature_analysis',
|
|
100
|
-
phase: 'technical-design',
|
|
101
|
-
expectedTargetStatus: 'technical_design',
|
|
102
|
-
description: 'CLI progressing to technical design',
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
currentStatus: 'technical_design',
|
|
106
|
-
phase: 'code-implementation',
|
|
107
|
-
expectedTargetStatus: 'code_implementation',
|
|
108
|
-
description: 'CLI progressing to code implementation',
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
currentStatus: 'functional_testing',
|
|
112
|
-
phase: 'deployment',
|
|
113
|
-
expectedTargetStatus: 'deployment',
|
|
114
|
-
description: 'CLI progressing to deployment after testing',
|
|
115
|
-
},
|
|
116
|
-
];
|
|
117
|
-
for (const scenario of validScenarios) {
|
|
118
|
-
const result = isForwardProgression(scenario.currentStatus, scenario.expectedTargetStatus);
|
|
119
|
-
assert.strictEqual(result, true, `Valid progression should be allowed: ${scenario.description}`);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
describe('Edge Cases and Boundary Conditions', () => {
|
|
124
|
-
it('should handle same-status transitions correctly', () => {
|
|
125
|
-
// Allow staying at the same status (for retries, re-runs, etc.)
|
|
126
|
-
for (const status of STATUS_PROGRESSION_ORDER) {
|
|
127
|
-
const result = isForwardProgression(status, status);
|
|
128
|
-
assert.strictEqual(result, true, `Should allow staying at status ${status}`);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
it('should handle first and last status edge cases', () => {
|
|
132
|
-
const firstStatus = STATUS_PROGRESSION_ORDER[0]; // 'backlog'
|
|
133
|
-
const lastStatus = STATUS_PROGRESSION_ORDER[STATUS_PROGRESSION_ORDER.length - 1]; // 'shipped'
|
|
134
|
-
// From first to last should be allowed
|
|
135
|
-
const forwardResult = isForwardProgression(firstStatus, lastStatus);
|
|
136
|
-
assert.strictEqual(forwardResult, true, 'Should allow progression from first to last status');
|
|
137
|
-
// From last to first should be prevented
|
|
138
|
-
const backwardResult = isForwardProgression(lastStatus, firstStatus);
|
|
139
|
-
assert.strictEqual(backwardResult, false, 'Should prevent regression from last to first status');
|
|
140
|
-
});
|
|
141
|
-
it('should validate progression order is strictly enforced', () => {
|
|
142
|
-
// Test every adjacent pair in the progression
|
|
143
|
-
for (let i = 0; i < STATUS_PROGRESSION_ORDER.length - 1; i++) {
|
|
144
|
-
const currentStatus = STATUS_PROGRESSION_ORDER[i];
|
|
145
|
-
const nextStatus = STATUS_PROGRESSION_ORDER[i + 1];
|
|
146
|
-
// Forward should be allowed
|
|
147
|
-
const forwardResult = isForwardProgression(currentStatus, nextStatus);
|
|
148
|
-
assert.strictEqual(forwardResult, true, `Should allow progression from ${currentStatus} to ${nextStatus}`);
|
|
149
|
-
// Backward should be prevented
|
|
150
|
-
const backwardResult = isForwardProgression(nextStatus, currentStatus);
|
|
151
|
-
assert.strictEqual(backwardResult, false, `Should prevent regression from ${nextStatus} to ${currentStatus}`);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
it('should handle testing workflow regression scenarios', () => {
|
|
155
|
-
// Testing has special rules - test specific edge cases
|
|
156
|
-
const testingScenarios = [
|
|
157
|
-
{
|
|
158
|
-
from: 'testing_passed',
|
|
159
|
-
to: 'testing_in_progress',
|
|
160
|
-
shouldAllow: false,
|
|
161
|
-
description: 'Should prevent going back from passed to in-progress',
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
from: 'testing_passed',
|
|
165
|
-
to: 'testing_failed',
|
|
166
|
-
shouldAllow: true,
|
|
167
|
-
description: 'Should allow going from passed to failed (possible if retested)',
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
from: 'testing_failed',
|
|
171
|
-
to: 'testing_passed',
|
|
172
|
-
shouldAllow: false,
|
|
173
|
-
description: 'Should prevent going from failed to passed (need to restart testing)',
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
from: 'testing_in_progress',
|
|
177
|
-
to: 'testing_failed',
|
|
178
|
-
shouldAllow: true,
|
|
179
|
-
description: 'Should allow marking in-progress tests as failed',
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
from: 'testing_in_progress',
|
|
183
|
-
to: 'testing_passed',
|
|
184
|
-
shouldAllow: true,
|
|
185
|
-
description: 'Should allow marking in-progress tests as passed',
|
|
186
|
-
},
|
|
187
|
-
];
|
|
188
|
-
for (const scenario of testingScenarios) {
|
|
189
|
-
const result = isForwardProgression(scenario.from, scenario.to);
|
|
190
|
-
assert.strictEqual(result, scenario.shouldAllow, scenario.description);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
it('should prevent skipping multiple phases backwards', () => {
|
|
194
|
-
// Test skipping multiple phases in regression
|
|
195
|
-
const skipBackwardTests = [
|
|
196
|
-
{
|
|
197
|
-
from: 'shipped',
|
|
198
|
-
to: 'feature_analysis',
|
|
199
|
-
skipCount: 'many phases',
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
from: 'code_implementation',
|
|
203
|
-
to: 'backlog',
|
|
204
|
-
skipCount: 'many phases',
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
from: 'deployment',
|
|
208
|
-
to: 'ready_for_dev',
|
|
209
|
-
skipCount: 'many phases',
|
|
210
|
-
},
|
|
211
|
-
];
|
|
212
|
-
for (const test of skipBackwardTests) {
|
|
213
|
-
const result = isForwardProgression(test.from, test.to);
|
|
214
|
-
assert.strictEqual(result, false, `Should prevent skipping ${test.skipCount} backwards from ${test.from} to ${test.to}`);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
it('should allow skipping multiple phases forward', () => {
|
|
218
|
-
// Test skipping phases in forward direction (should be allowed)
|
|
219
|
-
const skipForwardTests = [
|
|
220
|
-
{
|
|
221
|
-
from: 'backlog',
|
|
222
|
-
to: 'code_implementation',
|
|
223
|
-
description: 'Skip from backlog to code implementation',
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
from: 'feature_analysis',
|
|
227
|
-
to: 'deployment',
|
|
228
|
-
description: 'Skip from analysis to deployment',
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
from: 'technical_design',
|
|
232
|
-
to: 'shipped',
|
|
233
|
-
description: 'Skip from design to shipped',
|
|
234
|
-
},
|
|
235
|
-
];
|
|
236
|
-
for (const test of skipForwardTests) {
|
|
237
|
-
const result = isForwardProgression(test.from, test.to);
|
|
238
|
-
assert.strictEqual(result, true, `Should allow forward skip: ${test.description}`);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
describe('Real-world Workflow Scenarios', () => {
|
|
243
|
-
it('should handle hotfix scenario correctly', () => {
|
|
244
|
-
// Hotfix: shipped feature needs bug fixing
|
|
245
|
-
// Should allow progression to bug_fixing from shipped
|
|
246
|
-
const result = isForwardProgression('shipped', 'bug_fixing');
|
|
247
|
-
// Since bug_fixing comes before shipped in progression, this should be false
|
|
248
|
-
assert.strictEqual(result, false, 'Should prevent regression from shipped to bug_fixing (hotfixes need new feature entries)');
|
|
249
|
-
});
|
|
250
|
-
it('should handle feature refinement scenarios', () => {
|
|
251
|
-
// Feature refinement: going from code_implementation to code_refine
|
|
252
|
-
const result1 = isForwardProgression('code_implementation', 'code_refine');
|
|
253
|
-
assert.strictEqual(result1, true, 'Should allow progression from implementation to refinement');
|
|
254
|
-
// But not the other way around
|
|
255
|
-
const result2 = isForwardProgression('code_refine', 'code_implementation');
|
|
256
|
-
assert.strictEqual(result2, false, 'Should prevent regression from refinement to implementation');
|
|
257
|
-
});
|
|
258
|
-
it('should handle review cycle scenarios', () => {
|
|
259
|
-
// Review cycles: code_review -> pull_request -> code_review (if rejected)
|
|
260
|
-
const reviewToPrep = isForwardProgression('code_review', 'pull_request');
|
|
261
|
-
assert.strictEqual(reviewToPrep, true, 'Should allow progression from review to pull request');
|
|
262
|
-
// But not back from pull_request to code_review
|
|
263
|
-
const prepToReview = isForwardProgression('pull_request', 'code_review');
|
|
264
|
-
assert.strictEqual(prepToReview, false, 'Should prevent regression from pull request to code review');
|
|
265
|
-
});
|
|
266
|
-
it('should handle deployment rollback scenarios', () => {
|
|
267
|
-
// Deployment rollback: deployment -> functional_testing (to re-test)
|
|
268
|
-
const deployToTest = isForwardProgression('deployment', 'functional_testing');
|
|
269
|
-
assert.strictEqual(deployToTest, false, 'Should prevent regression from deployment to testing (rollbacks need new process)');
|
|
270
|
-
});
|
|
271
|
-
it('should validate complete workflow progression path', () => {
|
|
272
|
-
// Simulate a complete feature lifecycle
|
|
273
|
-
const completeWorkflow = [
|
|
274
|
-
'backlog',
|
|
275
|
-
'ready_for_dev',
|
|
276
|
-
'feature_analysis',
|
|
277
|
-
'feature_analysis_verification',
|
|
278
|
-
'technical_design',
|
|
279
|
-
'technical_design_verification',
|
|
280
|
-
'code_implementation',
|
|
281
|
-
'code_refine',
|
|
282
|
-
'code_review',
|
|
283
|
-
'pull_request',
|
|
284
|
-
'functional_testing',
|
|
285
|
-
'testing_in_progress',
|
|
286
|
-
'testing_passed',
|
|
287
|
-
'deployment',
|
|
288
|
-
'shipped',
|
|
289
|
-
];
|
|
290
|
-
// Test each step in the workflow
|
|
291
|
-
for (let i = 0; i < completeWorkflow.length - 1; i++) {
|
|
292
|
-
const currentStatus = completeWorkflow[i];
|
|
293
|
-
const nextStatus = completeWorkflow[i + 1];
|
|
294
|
-
const result = isForwardProgression(currentStatus, nextStatus);
|
|
295
|
-
assert.strictEqual(result, true, `Workflow step ${i + 1}: Should allow progression from ${currentStatus} to ${nextStatus}`);
|
|
296
|
-
}
|
|
297
|
-
// Test that you can't go backwards at any point in the workflow
|
|
298
|
-
for (let i = 1; i < completeWorkflow.length; i++) {
|
|
299
|
-
const currentStatus = completeWorkflow[i];
|
|
300
|
-
const previousStatus = completeWorkflow[i - 1];
|
|
301
|
-
const result = isForwardProgression(currentStatus, previousStatus);
|
|
302
|
-
assert.strictEqual(result, false, `Workflow regression check ${i}: Should prevent regression from ${currentStatus} to ${previousStatus}`);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
describe('Status Progression Order Validation', () => {
|
|
307
|
-
it('should ensure progression order matches business workflow', () => {
|
|
308
|
-
// Verify that the progression order makes business sense
|
|
309
|
-
const criticalStatusPositions = {
|
|
310
|
-
backlog: STATUS_PROGRESSION_ORDER.indexOf('backlog'),
|
|
311
|
-
ready_for_dev: STATUS_PROGRESSION_ORDER.indexOf('ready_for_dev'),
|
|
312
|
-
feature_analysis: STATUS_PROGRESSION_ORDER.indexOf('feature_analysis'),
|
|
313
|
-
technical_design: STATUS_PROGRESSION_ORDER.indexOf('technical_design'),
|
|
314
|
-
code_implementation: STATUS_PROGRESSION_ORDER.indexOf('code_implementation'),
|
|
315
|
-
functional_testing: STATUS_PROGRESSION_ORDER.indexOf('functional_testing'),
|
|
316
|
-
deployment: STATUS_PROGRESSION_ORDER.indexOf('deployment'),
|
|
317
|
-
shipped: STATUS_PROGRESSION_ORDER.indexOf('shipped'),
|
|
318
|
-
};
|
|
319
|
-
// Verify logical ordering
|
|
320
|
-
assert.ok(criticalStatusPositions.backlog < criticalStatusPositions.ready_for_dev, 'Backlog should come before ready_for_dev');
|
|
321
|
-
assert.ok(criticalStatusPositions.ready_for_dev < criticalStatusPositions.feature_analysis, 'Ready for dev should come before feature analysis');
|
|
322
|
-
assert.ok(criticalStatusPositions.feature_analysis < criticalStatusPositions.technical_design, 'Feature analysis should come before technical design');
|
|
323
|
-
assert.ok(criticalStatusPositions.technical_design < criticalStatusPositions.code_implementation, 'Technical design should come before code implementation');
|
|
324
|
-
assert.ok(criticalStatusPositions.code_implementation < criticalStatusPositions.functional_testing, 'Code implementation should come before functional testing');
|
|
325
|
-
assert.ok(criticalStatusPositions.functional_testing < criticalStatusPositions.deployment, 'Functional testing should come before deployment');
|
|
326
|
-
assert.ok(criticalStatusPositions.deployment < criticalStatusPositions.shipped, 'Deployment should come before shipped');
|
|
327
|
-
});
|
|
328
|
-
it('should prevent CLI from causing any backward movement', () => {
|
|
329
|
-
// This test specifically addresses the original issue:
|
|
330
|
-
// "After running CLI, in some phases, it update feature status to 'Backlog'"
|
|
331
|
-
// No matter what the current status is, CLI should NEVER be able to set it to backlog
|
|
332
|
-
for (const currentStatus of STATUS_PROGRESSION_ORDER.slice(1)) {
|
|
333
|
-
const result = isForwardProgression(currentStatus, 'backlog');
|
|
334
|
-
assert.strictEqual(result, false, `CLI should NEVER be able to regress ${currentStatus} to backlog`);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
});
|