edsger 0.41.3 → 0.42.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/.claude/settings.local.json +3 -23
- package/dist/commands/pr-resolve/index.d.ts +1 -0
- package/dist/commands/pr-resolve/index.js +1 -0
- package/dist/index.js +1 -0
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.d.ts +1 -0
- package/dist/phases/pr-resolve/__tests__/checklist-learner.test.js +157 -0
- package/dist/phases/pr-resolve/__tests__/types.test.d.ts +1 -0
- package/dist/phases/pr-resolve/__tests__/types.test.js +43 -0
- package/dist/phases/pr-resolve/checklist-learner.d.ts +28 -0
- package/dist/phases/pr-resolve/checklist-learner.js +128 -0
- package/dist/phases/pr-resolve/index.d.ts +4 -0
- package/dist/phases/pr-resolve/index.js +23 -5
- package/dist/phases/pr-resolve/types.d.ts +18 -0
- package/dist/phases/pr-resolve/types.js +14 -0
- package/dist/phases/pr-splitting/context.js +20 -15
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +4 -0
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +133 -0
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +4 -0
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +336 -0
- package/dist/services/lifecycle-agent/index.d.ts +24 -0
- package/dist/services/lifecycle-agent/index.js +25 -0
- package/dist/services/lifecycle-agent/phase-criteria.d.ts +57 -0
- package/dist/services/lifecycle-agent/phase-criteria.js +335 -0
- package/dist/services/lifecycle-agent/transition-rules.d.ts +60 -0
- package/dist/services/lifecycle-agent/transition-rules.js +184 -0
- package/dist/services/lifecycle-agent/types.d.ts +190 -0
- package/dist/services/lifecycle-agent/types.js +12 -0
- package/package.json +1 -1
- package/.env.local +0 -12
- 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
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Agent Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the AI-powered lifecycle agent that manages
|
|
5
|
+
* product feature lifecycle decisions: when to advance to the next phase,
|
|
6
|
+
* when to re-run the current phase, and when to escalate to a human.
|
|
7
|
+
*
|
|
8
|
+
* The lifecycle agent evaluates phase outputs against quality criteria
|
|
9
|
+
* and makes transition decisions, reducing reliance on manual human
|
|
10
|
+
* intervention while preserving the ability to escalate when needed.
|
|
11
|
+
*/
|
|
12
|
+
import type { FeatureStatus } from '../../types/index.js';
|
|
13
|
+
import type { ExecutionMode } from '../../types/pipeline.js';
|
|
14
|
+
/**
|
|
15
|
+
* Decision the lifecycle agent can make after evaluating a phase
|
|
16
|
+
*
|
|
17
|
+
* - advance: Phase output meets quality criteria, proceed to next phase
|
|
18
|
+
* - rerun: Phase output needs improvement, re-run with generated feedback
|
|
19
|
+
* - escalate: Agent is uncertain or output requires human judgment
|
|
20
|
+
*/
|
|
21
|
+
export type LifecycleDecision = 'advance' | 'rerun' | 'escalate';
|
|
22
|
+
/**
|
|
23
|
+
* Result of evaluating a single quality criterion
|
|
24
|
+
*/
|
|
25
|
+
export interface CriterionEvaluation {
|
|
26
|
+
/** Criterion identifier matching a CriterionDefinition.id */
|
|
27
|
+
readonly criterionId: string;
|
|
28
|
+
/** Score from 0-100 for this criterion */
|
|
29
|
+
readonly score: number;
|
|
30
|
+
/** Whether the criterion meets its minimum threshold */
|
|
31
|
+
readonly passed: boolean;
|
|
32
|
+
/** Explanation of the evaluation result */
|
|
33
|
+
readonly reasoning: string;
|
|
34
|
+
/** Specific issues found, if any */
|
|
35
|
+
readonly issues?: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Complete evaluation result for a phase execution
|
|
39
|
+
* This is the primary output of the lifecycle agent's evaluation step
|
|
40
|
+
*/
|
|
41
|
+
export interface PhaseEvaluationResult {
|
|
42
|
+
/** The phase that was evaluated */
|
|
43
|
+
readonly phase: string;
|
|
44
|
+
/** Feature being evaluated */
|
|
45
|
+
readonly featureId: string;
|
|
46
|
+
/** Overall quality score (weighted average of criteria scores, 0-100) */
|
|
47
|
+
readonly qualityScore: number;
|
|
48
|
+
/** Decision: advance, rerun, or escalate */
|
|
49
|
+
readonly decision: LifecycleDecision;
|
|
50
|
+
/** Agent's confidence in its decision (0-100) */
|
|
51
|
+
readonly confidence: number;
|
|
52
|
+
/** Individual criterion evaluations */
|
|
53
|
+
readonly criteria: readonly CriterionEvaluation[];
|
|
54
|
+
/** Summary reasoning for the decision */
|
|
55
|
+
readonly reasoning: string;
|
|
56
|
+
/** If decision is 'rerun', suggested feedbacks to inject */
|
|
57
|
+
readonly suggestedFeedbacks?: readonly SuggestedFeedback[];
|
|
58
|
+
/** How many times this phase has been re-run in the current cycle */
|
|
59
|
+
readonly rerunCount: number;
|
|
60
|
+
/** Timestamp of the evaluation */
|
|
61
|
+
readonly evaluatedAt: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Feedback suggested by the lifecycle agent when deciding to re-run a phase
|
|
65
|
+
* These mirror the existing Feedback structure so they can be injected
|
|
66
|
+
* into the feedback system seamlessly
|
|
67
|
+
*/
|
|
68
|
+
export interface SuggestedFeedback {
|
|
69
|
+
/** Type of feedback, matching existing FeedbackType */
|
|
70
|
+
readonly feedbackType: 'requirement' | 'constraint' | 'preference' | 'context' | 'quality_criteria' | 'issue' | 'suggestion';
|
|
71
|
+
/** Short title describing the feedback */
|
|
72
|
+
readonly title: string;
|
|
73
|
+
/** Detailed content of the feedback */
|
|
74
|
+
readonly content: string;
|
|
75
|
+
/** Priority from 1-10 (higher = more important) */
|
|
76
|
+
readonly priority: number;
|
|
77
|
+
/** Which phase this feedback targets */
|
|
78
|
+
readonly phase: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Definition of a quality criterion for phase evaluation
|
|
82
|
+
* Each phase has its own set of criteria that the agent uses to evaluate outputs
|
|
83
|
+
*/
|
|
84
|
+
export interface CriterionDefinition {
|
|
85
|
+
/** Unique identifier for this criterion */
|
|
86
|
+
readonly id: string;
|
|
87
|
+
/** Human-readable name */
|
|
88
|
+
readonly name: string;
|
|
89
|
+
/** Description of what this criterion measures */
|
|
90
|
+
readonly description: string;
|
|
91
|
+
/** Relative importance weight (0-1, all weights for a phase should sum to ~1) */
|
|
92
|
+
readonly weight: number;
|
|
93
|
+
/** Minimum score (0-100) needed to pass this criterion */
|
|
94
|
+
readonly minimumScore: number;
|
|
95
|
+
/** Prompt instructions for how the AI should evaluate this criterion */
|
|
96
|
+
readonly evaluationGuidance: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Quality criteria configuration for a specific phase
|
|
100
|
+
* Defines the thresholds and criteria used to evaluate that phase's outputs
|
|
101
|
+
*/
|
|
102
|
+
export interface PhaseQualityCriteria {
|
|
103
|
+
/** Phase name (underscore format, e.g., 'user_stories_analysis') */
|
|
104
|
+
readonly phase: string;
|
|
105
|
+
/** Minimum overall quality score needed to auto-advance (0-100) */
|
|
106
|
+
readonly advanceThreshold: number;
|
|
107
|
+
/** Overall score below which the agent should escalate to human (0-100) */
|
|
108
|
+
readonly escalateThreshold: number;
|
|
109
|
+
/** Maximum number of automatic re-runs before escalating */
|
|
110
|
+
readonly maxAutoRetries: number;
|
|
111
|
+
/** Individual quality criteria for this phase */
|
|
112
|
+
readonly criteria: readonly CriterionDefinition[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A transition rule that maps phase evaluation outcomes to actions
|
|
116
|
+
*/
|
|
117
|
+
export interface TransitionRule {
|
|
118
|
+
/** Phase this rule applies to (underscore format) */
|
|
119
|
+
readonly fromPhase: string;
|
|
120
|
+
/** Target phase if advancing */
|
|
121
|
+
readonly toPhase: string;
|
|
122
|
+
/** Execution mode to use when advancing to the target phase */
|
|
123
|
+
readonly executionMode: ExecutionMode;
|
|
124
|
+
/** Feature status to set when advancing */
|
|
125
|
+
readonly targetStatus: FeatureStatus;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Configuration for the lifecycle agent
|
|
129
|
+
* Controls behavior and thresholds for automated lifecycle management
|
|
130
|
+
*/
|
|
131
|
+
export interface LifecycleAgentConfig {
|
|
132
|
+
/** Whether the lifecycle agent is enabled for this product */
|
|
133
|
+
readonly enabled: boolean;
|
|
134
|
+
/** Allow agent to auto-advance phases without human approval */
|
|
135
|
+
readonly autoAdvanceEnabled: boolean;
|
|
136
|
+
/** Allow agent to auto-rerun phases with generated feedback */
|
|
137
|
+
readonly autoRerunEnabled: boolean;
|
|
138
|
+
/** Global maximum auto-retries (overrides per-phase if lower) */
|
|
139
|
+
readonly maxAutoRetries: number;
|
|
140
|
+
/** When confidence is below this threshold, always escalate (0-100) */
|
|
141
|
+
readonly confidenceThreshold: number;
|
|
142
|
+
/** Per-phase quality criteria overrides (falls back to defaults if not set) */
|
|
143
|
+
readonly phaseOverrides?: Partial<Record<string, Partial<PhaseQualityCriteria>>>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Runtime state for the lifecycle agent managing a specific feature
|
|
147
|
+
* Tracks re-run counts and evaluation history
|
|
148
|
+
*/
|
|
149
|
+
export interface LifecycleAgentState {
|
|
150
|
+
/** Feature being managed */
|
|
151
|
+
readonly featureId: string;
|
|
152
|
+
/** Number of re-runs per phase in current cycle */
|
|
153
|
+
readonly rerunCounts: Readonly<Record<string, number>>;
|
|
154
|
+
/** History of evaluation results */
|
|
155
|
+
readonly evaluationHistory: readonly PhaseEvaluationResult[];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Input to the lifecycle agent's evaluate function
|
|
159
|
+
* Contains all context needed to evaluate a phase's output
|
|
160
|
+
*/
|
|
161
|
+
export interface PhaseEvaluationInput {
|
|
162
|
+
/** Feature ID */
|
|
163
|
+
readonly featureId: string;
|
|
164
|
+
/** Phase that was just executed */
|
|
165
|
+
readonly phase: string;
|
|
166
|
+
/** Current feature status */
|
|
167
|
+
readonly currentStatus: FeatureStatus;
|
|
168
|
+
/** The phase execution result data (varies by phase) */
|
|
169
|
+
readonly phaseOutput: unknown;
|
|
170
|
+
/** Quality criteria to evaluate against */
|
|
171
|
+
readonly qualityCriteria: PhaseQualityCriteria;
|
|
172
|
+
/** Current re-run count for this phase */
|
|
173
|
+
readonly rerunCount: number;
|
|
174
|
+
/** Agent configuration */
|
|
175
|
+
readonly config: LifecycleAgentConfig;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Output from the lifecycle agent's decide function
|
|
179
|
+
* Contains the decision and all information needed to execute it
|
|
180
|
+
*/
|
|
181
|
+
export interface LifecycleDecisionResult {
|
|
182
|
+
/** The evaluation that led to this decision */
|
|
183
|
+
readonly evaluation: PhaseEvaluationResult;
|
|
184
|
+
/** The transition to execute if advancing */
|
|
185
|
+
readonly transition?: TransitionRule;
|
|
186
|
+
/** Execution mode to use (for both advance and rerun scenarios) */
|
|
187
|
+
readonly executionMode?: ExecutionMode;
|
|
188
|
+
/** Feature status to set */
|
|
189
|
+
readonly targetStatus?: FeatureStatus;
|
|
190
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Agent Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the AI-powered lifecycle agent that manages
|
|
5
|
+
* product feature lifecycle decisions: when to advance to the next phase,
|
|
6
|
+
* when to re-run the current phase, and when to escalate to a human.
|
|
7
|
+
*
|
|
8
|
+
* The lifecycle agent evaluates phase outputs against quality criteria
|
|
9
|
+
* and makes transition decisions, reducing reliance on manual human
|
|
10
|
+
* intervention while preserving the ability to escalate when needed.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/package.json
CHANGED
package/.env.local
DELETED
|
@@ -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
|
-
});
|