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,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for phase quality criteria definitions
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { DEFAULT_PHASE_CRITERIA, USER_STORIES_ANALYSIS_CRITERIA, TEST_CASES_ANALYSIS_CRITERIA, TECHNICAL_DESIGN_CRITERIA, BRANCH_PLANNING_CRITERIA, CODE_IMPLEMENTATION_CRITERIA, FUNCTIONAL_TESTING_CRITERIA, CODE_REVIEW_CRITERIA, getPhaseQualityCriteria, } from '../phase-criteria.js';
|
|
7
|
+
describe('Phase Quality Criteria', () => {
|
|
8
|
+
describe('DEFAULT_PHASE_CRITERIA', () => {
|
|
9
|
+
it('should cover all evaluable phases', () => {
|
|
10
|
+
const expectedPhases = [
|
|
11
|
+
'user_stories_analysis',
|
|
12
|
+
'test_cases_analysis',
|
|
13
|
+
'technical_design',
|
|
14
|
+
'branch_planning',
|
|
15
|
+
'code_implementation',
|
|
16
|
+
'functional_testing',
|
|
17
|
+
'code_review',
|
|
18
|
+
];
|
|
19
|
+
for (const phase of expectedPhases) {
|
|
20
|
+
assert.ok(phase in DEFAULT_PHASE_CRITERIA, `Should have criteria for phase: ${phase}`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
it('should have consistent phase names in criteria objects', () => {
|
|
24
|
+
for (const [key, criteria] of Object.entries(DEFAULT_PHASE_CRITERIA)) {
|
|
25
|
+
assert.strictEqual(criteria.phase, key, `Criteria for ${key} should have matching phase name`);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('Criteria Structural Validity', () => {
|
|
30
|
+
const allCriteria = [
|
|
31
|
+
USER_STORIES_ANALYSIS_CRITERIA,
|
|
32
|
+
TEST_CASES_ANALYSIS_CRITERIA,
|
|
33
|
+
TECHNICAL_DESIGN_CRITERIA,
|
|
34
|
+
BRANCH_PLANNING_CRITERIA,
|
|
35
|
+
CODE_IMPLEMENTATION_CRITERIA,
|
|
36
|
+
FUNCTIONAL_TESTING_CRITERIA,
|
|
37
|
+
CODE_REVIEW_CRITERIA,
|
|
38
|
+
];
|
|
39
|
+
for (const phaseCriteria of allCriteria) {
|
|
40
|
+
describe(phaseCriteria.phase, () => {
|
|
41
|
+
it('should have advanceThreshold > escalateThreshold', () => {
|
|
42
|
+
assert.ok(phaseCriteria.advanceThreshold > phaseCriteria.escalateThreshold, `advanceThreshold (${phaseCriteria.advanceThreshold}) should be > escalateThreshold (${phaseCriteria.escalateThreshold})`);
|
|
43
|
+
});
|
|
44
|
+
it('should have thresholds in valid range (0-100)', () => {
|
|
45
|
+
assert.ok(phaseCriteria.advanceThreshold >= 0);
|
|
46
|
+
assert.ok(phaseCriteria.advanceThreshold <= 100);
|
|
47
|
+
assert.ok(phaseCriteria.escalateThreshold >= 0);
|
|
48
|
+
assert.ok(phaseCriteria.escalateThreshold <= 100);
|
|
49
|
+
});
|
|
50
|
+
it('should have maxAutoRetries >= 1', () => {
|
|
51
|
+
assert.ok(phaseCriteria.maxAutoRetries >= 1, `maxAutoRetries should be >= 1, got ${phaseCriteria.maxAutoRetries}`);
|
|
52
|
+
});
|
|
53
|
+
it('should have at least one criterion', () => {
|
|
54
|
+
assert.ok(phaseCriteria.criteria.length > 0, 'Should have at least one criterion');
|
|
55
|
+
});
|
|
56
|
+
it('should have criteria weights that approximately sum to 1', () => {
|
|
57
|
+
const totalWeight = phaseCriteria.criteria.reduce((sum, c) => sum + c.weight, 0);
|
|
58
|
+
assert.ok(Math.abs(totalWeight - 1.0) < 0.01, `Weights should sum to ~1.0, got ${totalWeight}`);
|
|
59
|
+
});
|
|
60
|
+
it('should have unique criterion IDs', () => {
|
|
61
|
+
const ids = phaseCriteria.criteria.map((c) => c.id);
|
|
62
|
+
const uniqueIds = new Set(ids);
|
|
63
|
+
assert.strictEqual(ids.length, uniqueIds.size, 'Criterion IDs should be unique');
|
|
64
|
+
});
|
|
65
|
+
it('should have valid criterion weights (0 < weight <= 1)', () => {
|
|
66
|
+
for (const criterion of phaseCriteria.criteria) {
|
|
67
|
+
assert.ok(criterion.weight > 0 && criterion.weight <= 1, `Weight for ${criterion.id} should be between 0 and 1, got ${criterion.weight}`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
it('should have valid minimum scores (0-100)', () => {
|
|
71
|
+
for (const criterion of phaseCriteria.criteria) {
|
|
72
|
+
assert.ok(criterion.minimumScore >= 0 && criterion.minimumScore <= 100, `minimumScore for ${criterion.id} should be 0-100, got ${criterion.minimumScore}`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
it('should have non-empty evaluation guidance', () => {
|
|
76
|
+
for (const criterion of phaseCriteria.criteria) {
|
|
77
|
+
assert.ok(criterion.evaluationGuidance.length > 0, `Criterion ${criterion.id} should have evaluation guidance`);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
describe('getPhaseQualityCriteria', () => {
|
|
84
|
+
it('should return default criteria for known phases', () => {
|
|
85
|
+
const criteria = getPhaseQualityCriteria('user_stories_analysis');
|
|
86
|
+
assert.ok(criteria);
|
|
87
|
+
assert.strictEqual(criteria.phase, 'user_stories_analysis');
|
|
88
|
+
assert.strictEqual(criteria.advanceThreshold, USER_STORIES_ANALYSIS_CRITERIA.advanceThreshold);
|
|
89
|
+
});
|
|
90
|
+
it('should return null for unknown phases', () => {
|
|
91
|
+
const criteria = getPhaseQualityCriteria('nonexistent_phase');
|
|
92
|
+
assert.strictEqual(criteria, null);
|
|
93
|
+
});
|
|
94
|
+
it('should apply overrides when provided', () => {
|
|
95
|
+
const criteria = getPhaseQualityCriteria('user_stories_analysis', {
|
|
96
|
+
user_stories_analysis: {
|
|
97
|
+
advanceThreshold: 90,
|
|
98
|
+
maxAutoRetries: 5,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
assert.ok(criteria);
|
|
102
|
+
assert.strictEqual(criteria.advanceThreshold, 90);
|
|
103
|
+
assert.strictEqual(criteria.maxAutoRetries, 5);
|
|
104
|
+
// Non-overridden values should remain from defaults
|
|
105
|
+
assert.strictEqual(criteria.escalateThreshold, USER_STORIES_ANALYSIS_CRITERIA.escalateThreshold);
|
|
106
|
+
});
|
|
107
|
+
it('should return defaults when override map does not include the phase', () => {
|
|
108
|
+
const criteria = getPhaseQualityCriteria('technical_design', {
|
|
109
|
+
user_stories_analysis: { advanceThreshold: 90 },
|
|
110
|
+
});
|
|
111
|
+
assert.ok(criteria);
|
|
112
|
+
assert.strictEqual(criteria.advanceThreshold, TECHNICAL_DESIGN_CRITERIA.advanceThreshold);
|
|
113
|
+
});
|
|
114
|
+
it('should override criteria array when provided', () => {
|
|
115
|
+
const customCriteria = [
|
|
116
|
+
{
|
|
117
|
+
id: 'custom_1',
|
|
118
|
+
name: 'Custom',
|
|
119
|
+
description: 'A custom criterion',
|
|
120
|
+
weight: 1.0,
|
|
121
|
+
minimumScore: 50,
|
|
122
|
+
evaluationGuidance: 'Custom guidance',
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
const criteria = getPhaseQualityCriteria('technical_design', {
|
|
126
|
+
technical_design: { criteria: customCriteria },
|
|
127
|
+
});
|
|
128
|
+
assert.ok(criteria);
|
|
129
|
+
assert.strictEqual(criteria.criteria.length, 1);
|
|
130
|
+
assert.strictEqual(criteria.criteria[0].id, 'custom_1');
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for lifecycle agent transition rules and decision logic
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import assert from 'node:assert';
|
|
6
|
+
import { DEFAULT_TRANSITION_RULES, getTransitionRule, getRerunExecutionMode, getVerificationStatus, calculateQualityScore, determineDecision, buildDecisionResult, DEFAULT_LIFECYCLE_AGENT_CONFIG, } from '../transition-rules.js';
|
|
7
|
+
// Helpers to create test fixtures
|
|
8
|
+
function makeCriteria(overrides) {
|
|
9
|
+
return {
|
|
10
|
+
phase: 'test_phase',
|
|
11
|
+
advanceThreshold: 75,
|
|
12
|
+
escalateThreshold: 40,
|
|
13
|
+
maxAutoRetries: 2,
|
|
14
|
+
criteria: [
|
|
15
|
+
{
|
|
16
|
+
id: 'c1',
|
|
17
|
+
name: 'Criterion 1',
|
|
18
|
+
description: 'Test criterion',
|
|
19
|
+
weight: 0.6,
|
|
20
|
+
minimumScore: 50,
|
|
21
|
+
evaluationGuidance: 'Evaluate this.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'c2',
|
|
25
|
+
name: 'Criterion 2',
|
|
26
|
+
description: 'Another test criterion',
|
|
27
|
+
weight: 0.4,
|
|
28
|
+
minimumScore: 50,
|
|
29
|
+
evaluationGuidance: 'Evaluate this too.',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
...overrides,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function makeConfig(overrides) {
|
|
36
|
+
return {
|
|
37
|
+
...DEFAULT_LIFECYCLE_AGENT_CONFIG,
|
|
38
|
+
...overrides,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function makeEvaluation(overrides) {
|
|
42
|
+
return {
|
|
43
|
+
phase: 'user_stories_analysis',
|
|
44
|
+
featureId: 'feat-123',
|
|
45
|
+
qualityScore: 80,
|
|
46
|
+
decision: 'advance',
|
|
47
|
+
confidence: 85,
|
|
48
|
+
criteria: [],
|
|
49
|
+
reasoning: 'Test evaluation',
|
|
50
|
+
rerunCount: 0,
|
|
51
|
+
evaluatedAt: new Date().toISOString(),
|
|
52
|
+
...overrides,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
describe('Transition Rules', () => {
|
|
56
|
+
describe('DEFAULT_TRANSITION_RULES', () => {
|
|
57
|
+
it('should cover all major phases', () => {
|
|
58
|
+
const phases = DEFAULT_TRANSITION_RULES.map((r) => r.fromPhase);
|
|
59
|
+
const expectedPhases = [
|
|
60
|
+
'user_stories_analysis',
|
|
61
|
+
'test_cases_analysis',
|
|
62
|
+
'technical_design',
|
|
63
|
+
'branch_planning',
|
|
64
|
+
'code_implementation',
|
|
65
|
+
'functional_testing',
|
|
66
|
+
'code_review',
|
|
67
|
+
];
|
|
68
|
+
for (const phase of expectedPhases) {
|
|
69
|
+
assert.ok(phases.includes(phase), `Should have transition rule for phase: ${phase}`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
it('should form a connected chain from user_stories_analysis to code_refine', () => {
|
|
73
|
+
let current = 'user_stories_analysis';
|
|
74
|
+
const visited = [current];
|
|
75
|
+
while (true) {
|
|
76
|
+
const rule = DEFAULT_TRANSITION_RULES.find((r) => r.fromPhase === current);
|
|
77
|
+
if (!rule)
|
|
78
|
+
break;
|
|
79
|
+
current = rule.toPhase;
|
|
80
|
+
visited.push(current);
|
|
81
|
+
}
|
|
82
|
+
assert.ok(visited.length > 1, 'Should form a chain of transitions');
|
|
83
|
+
assert.strictEqual(visited[0], 'user_stories_analysis', 'Chain should start at user_stories_analysis');
|
|
84
|
+
assert.strictEqual(visited[visited.length - 1], 'code_refine', 'Chain should end at code_refine');
|
|
85
|
+
});
|
|
86
|
+
it('should have valid execution modes', () => {
|
|
87
|
+
for (const rule of DEFAULT_TRANSITION_RULES) {
|
|
88
|
+
assert.ok(typeof rule.executionMode === 'string', `Rule for ${rule.fromPhase} should have a string executionMode`);
|
|
89
|
+
assert.ok(rule.executionMode.length > 0, `Rule for ${rule.fromPhase} should have non-empty executionMode`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('getTransitionRule', () => {
|
|
94
|
+
it('should return the correct rule for known phases', () => {
|
|
95
|
+
const rule = getTransitionRule('user_stories_analysis');
|
|
96
|
+
assert.ok(rule);
|
|
97
|
+
assert.strictEqual(rule.toPhase, 'test_cases_analysis');
|
|
98
|
+
assert.strictEqual(rule.executionMode, 'only_test_cases_analysis');
|
|
99
|
+
});
|
|
100
|
+
it('should return undefined for unknown phases', () => {
|
|
101
|
+
const rule = getTransitionRule('nonexistent_phase');
|
|
102
|
+
assert.strictEqual(rule, undefined);
|
|
103
|
+
});
|
|
104
|
+
it('should return correct transition for each phase', () => {
|
|
105
|
+
const expectations = {
|
|
106
|
+
user_stories_analysis: 'test_cases_analysis',
|
|
107
|
+
test_cases_analysis: 'technical_design',
|
|
108
|
+
technical_design: 'branch_planning',
|
|
109
|
+
branch_planning: 'code_implementation',
|
|
110
|
+
code_implementation: 'functional_testing',
|
|
111
|
+
functional_testing: 'code_review',
|
|
112
|
+
code_review: 'code_refine',
|
|
113
|
+
};
|
|
114
|
+
for (const [from, expectedTo] of Object.entries(expectations)) {
|
|
115
|
+
const rule = getTransitionRule(from);
|
|
116
|
+
assert.ok(rule, `Should have rule for ${from}`);
|
|
117
|
+
assert.strictEqual(rule.toPhase, expectedTo, `${from} should transition to ${expectedTo}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('getRerunExecutionMode', () => {
|
|
122
|
+
it('should return only_* mode for known phases', () => {
|
|
123
|
+
assert.strictEqual(getRerunExecutionMode('user_stories_analysis'), 'only_user_stories_analysis');
|
|
124
|
+
assert.strictEqual(getRerunExecutionMode('technical_design'), 'only_technical_design');
|
|
125
|
+
assert.strictEqual(getRerunExecutionMode('code_implementation'), 'only_code_implementation');
|
|
126
|
+
});
|
|
127
|
+
it('should return full_pipeline for unknown phases', () => {
|
|
128
|
+
assert.strictEqual(getRerunExecutionMode('nonexistent'), 'full_pipeline');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe('getVerificationStatus', () => {
|
|
132
|
+
it('should return verification status for phases that have one', () => {
|
|
133
|
+
assert.strictEqual(getVerificationStatus('user_stories_analysis'), 'user_stories_analysis_verification');
|
|
134
|
+
assert.strictEqual(getVerificationStatus('technical_design'), 'technical_design_verification');
|
|
135
|
+
assert.strictEqual(getVerificationStatus('code_implementation'), 'code_implementation_verification');
|
|
136
|
+
});
|
|
137
|
+
it('should return undefined for phases without verification', () => {
|
|
138
|
+
assert.strictEqual(getVerificationStatus('functional_testing'), undefined);
|
|
139
|
+
assert.strictEqual(getVerificationStatus('code_review'), undefined);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('Quality Score Calculation', () => {
|
|
144
|
+
describe('calculateQualityScore', () => {
|
|
145
|
+
const criteria = makeCriteria();
|
|
146
|
+
it('should calculate weighted average correctly', () => {
|
|
147
|
+
const evaluations = [
|
|
148
|
+
{
|
|
149
|
+
criterionId: 'c1',
|
|
150
|
+
score: 80,
|
|
151
|
+
passed: true,
|
|
152
|
+
reasoning: 'Good',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
criterionId: 'c2',
|
|
156
|
+
score: 60,
|
|
157
|
+
passed: true,
|
|
158
|
+
reasoning: 'Okay',
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
const score = calculateQualityScore(evaluations, criteria);
|
|
162
|
+
// (80 * 0.6 + 60 * 0.4) / (0.6 + 0.4) = (48 + 24) / 1 = 72
|
|
163
|
+
assert.strictEqual(score, 72);
|
|
164
|
+
});
|
|
165
|
+
it('should return 0 for empty evaluations', () => {
|
|
166
|
+
const score = calculateQualityScore([], criteria);
|
|
167
|
+
assert.strictEqual(score, 0);
|
|
168
|
+
});
|
|
169
|
+
it('should ignore evaluations for unknown criteria', () => {
|
|
170
|
+
const evaluations = [
|
|
171
|
+
{
|
|
172
|
+
criterionId: 'c1',
|
|
173
|
+
score: 80,
|
|
174
|
+
passed: true,
|
|
175
|
+
reasoning: 'Good',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
criterionId: 'unknown',
|
|
179
|
+
score: 10,
|
|
180
|
+
passed: false,
|
|
181
|
+
reasoning: 'Unknown',
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
const score = calculateQualityScore(evaluations, criteria);
|
|
185
|
+
// Only c1 is counted: (80 * 0.6) / 0.6 = 80
|
|
186
|
+
assert.strictEqual(score, 80);
|
|
187
|
+
});
|
|
188
|
+
it('should handle perfect scores', () => {
|
|
189
|
+
const evaluations = [
|
|
190
|
+
{
|
|
191
|
+
criterionId: 'c1',
|
|
192
|
+
score: 100,
|
|
193
|
+
passed: true,
|
|
194
|
+
reasoning: 'Perfect',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
criterionId: 'c2',
|
|
198
|
+
score: 100,
|
|
199
|
+
passed: true,
|
|
200
|
+
reasoning: 'Perfect',
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
const score = calculateQualityScore(evaluations, criteria);
|
|
204
|
+
assert.strictEqual(score, 100);
|
|
205
|
+
});
|
|
206
|
+
it('should handle zero scores', () => {
|
|
207
|
+
const evaluations = [
|
|
208
|
+
{
|
|
209
|
+
criterionId: 'c1',
|
|
210
|
+
score: 0,
|
|
211
|
+
passed: false,
|
|
212
|
+
reasoning: 'Failed',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
criterionId: 'c2',
|
|
216
|
+
score: 0,
|
|
217
|
+
passed: false,
|
|
218
|
+
reasoning: 'Failed',
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
const score = calculateQualityScore(evaluations, criteria);
|
|
222
|
+
assert.strictEqual(score, 0);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe('Decision Logic', () => {
|
|
227
|
+
describe('determineDecision', () => {
|
|
228
|
+
const criteria = makeCriteria();
|
|
229
|
+
const config = makeConfig();
|
|
230
|
+
it('should return advance when quality meets threshold', () => {
|
|
231
|
+
const decision = determineDecision(80, 85, 0, criteria, config);
|
|
232
|
+
assert.strictEqual(decision, 'advance');
|
|
233
|
+
});
|
|
234
|
+
it('should return escalate when quality is below escalate threshold', () => {
|
|
235
|
+
const decision = determineDecision(30, 85, 0, criteria, config);
|
|
236
|
+
assert.strictEqual(decision, 'escalate');
|
|
237
|
+
});
|
|
238
|
+
it('should return rerun when quality is between thresholds', () => {
|
|
239
|
+
const decision = determineDecision(55, 85, 0, criteria, config);
|
|
240
|
+
assert.strictEqual(decision, 'rerun');
|
|
241
|
+
});
|
|
242
|
+
it('should escalate when confidence is below threshold', () => {
|
|
243
|
+
const decision = determineDecision(80, 40, 0, criteria, config);
|
|
244
|
+
assert.strictEqual(decision, 'escalate');
|
|
245
|
+
});
|
|
246
|
+
it('should escalate when max retries exhausted', () => {
|
|
247
|
+
const decision = determineDecision(55, 85, 2, criteria, config);
|
|
248
|
+
assert.strictEqual(decision, 'escalate');
|
|
249
|
+
});
|
|
250
|
+
it('should use the lower of phase and config max retries', () => {
|
|
251
|
+
const strictConfig = makeConfig({ maxAutoRetries: 1 });
|
|
252
|
+
const decision = determineDecision(55, 85, 1, criteria, strictConfig);
|
|
253
|
+
assert.strictEqual(decision, 'escalate');
|
|
254
|
+
});
|
|
255
|
+
it('should advance at exactly the advance threshold', () => {
|
|
256
|
+
const decision = determineDecision(75, 85, 0, criteria, config);
|
|
257
|
+
assert.strictEqual(decision, 'advance');
|
|
258
|
+
});
|
|
259
|
+
it('should escalate at exactly the escalate threshold', () => {
|
|
260
|
+
// At exactly 40 (escalateThreshold), the score is NOT below it,
|
|
261
|
+
// so it falls to the rerun case
|
|
262
|
+
const decision = determineDecision(40, 85, 0, criteria, config);
|
|
263
|
+
assert.strictEqual(decision, 'rerun');
|
|
264
|
+
});
|
|
265
|
+
it('should escalate just below the escalate threshold', () => {
|
|
266
|
+
const decision = determineDecision(39, 85, 0, criteria, config);
|
|
267
|
+
assert.strictEqual(decision, 'escalate');
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
describe('buildDecisionResult', () => {
|
|
271
|
+
it('should include transition rule when advancing', () => {
|
|
272
|
+
const evaluation = makeEvaluation({
|
|
273
|
+
phase: 'user_stories_analysis',
|
|
274
|
+
decision: 'advance',
|
|
275
|
+
});
|
|
276
|
+
const result = buildDecisionResult(evaluation);
|
|
277
|
+
assert.ok(result.transition);
|
|
278
|
+
assert.strictEqual(result.transition.toPhase, 'test_cases_analysis');
|
|
279
|
+
assert.strictEqual(result.executionMode, 'only_test_cases_analysis');
|
|
280
|
+
assert.strictEqual(result.targetStatus, 'test_cases_analysis');
|
|
281
|
+
});
|
|
282
|
+
it('should include rerun execution mode when rerunning', () => {
|
|
283
|
+
const evaluation = makeEvaluation({
|
|
284
|
+
phase: 'technical_design',
|
|
285
|
+
decision: 'rerun',
|
|
286
|
+
});
|
|
287
|
+
const result = buildDecisionResult(evaluation);
|
|
288
|
+
assert.strictEqual(result.transition, undefined);
|
|
289
|
+
assert.strictEqual(result.executionMode, 'only_technical_design');
|
|
290
|
+
assert.strictEqual(result.targetStatus, undefined);
|
|
291
|
+
});
|
|
292
|
+
it('should not include execution mode when escalating', () => {
|
|
293
|
+
const evaluation = makeEvaluation({
|
|
294
|
+
phase: 'code_implementation',
|
|
295
|
+
decision: 'escalate',
|
|
296
|
+
});
|
|
297
|
+
const result = buildDecisionResult(evaluation);
|
|
298
|
+
assert.strictEqual(result.transition, undefined);
|
|
299
|
+
assert.strictEqual(result.executionMode, undefined);
|
|
300
|
+
assert.strictEqual(result.targetStatus, undefined);
|
|
301
|
+
});
|
|
302
|
+
it('should preserve the evaluation in the result', () => {
|
|
303
|
+
const evaluation = makeEvaluation({
|
|
304
|
+
phase: 'test_cases_analysis',
|
|
305
|
+
qualityScore: 60,
|
|
306
|
+
decision: 'rerun',
|
|
307
|
+
reasoning: 'Needs improvement',
|
|
308
|
+
});
|
|
309
|
+
const result = buildDecisionResult(evaluation);
|
|
310
|
+
assert.strictEqual(result.evaluation, evaluation);
|
|
311
|
+
assert.strictEqual(result.evaluation.qualityScore, 60);
|
|
312
|
+
assert.strictEqual(result.evaluation.reasoning, 'Needs improvement');
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
describe('Default Configuration', () => {
|
|
317
|
+
describe('DEFAULT_LIFECYCLE_AGENT_CONFIG', () => {
|
|
318
|
+
it('should be disabled by default', () => {
|
|
319
|
+
assert.strictEqual(DEFAULT_LIFECYCLE_AGENT_CONFIG.enabled, false);
|
|
320
|
+
});
|
|
321
|
+
it('should disable auto-advance by default', () => {
|
|
322
|
+
assert.strictEqual(DEFAULT_LIFECYCLE_AGENT_CONFIG.autoAdvanceEnabled, false);
|
|
323
|
+
});
|
|
324
|
+
it('should enable auto-rerun by default', () => {
|
|
325
|
+
assert.strictEqual(DEFAULT_LIFECYCLE_AGENT_CONFIG.autoRerunEnabled, true);
|
|
326
|
+
});
|
|
327
|
+
it('should have a reasonable confidence threshold', () => {
|
|
328
|
+
assert.ok(DEFAULT_LIFECYCLE_AGENT_CONFIG.confidenceThreshold >= 50);
|
|
329
|
+
assert.ok(DEFAULT_LIFECYCLE_AGENT_CONFIG.confidenceThreshold <= 80);
|
|
330
|
+
});
|
|
331
|
+
it('should have a reasonable max retries', () => {
|
|
332
|
+
assert.ok(DEFAULT_LIFECYCLE_AGENT_CONFIG.maxAutoRetries >= 1);
|
|
333
|
+
assert.ok(DEFAULT_LIFECYCLE_AGENT_CONFIG.maxAutoRetries <= 5);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Agent Module
|
|
3
|
+
*
|
|
4
|
+
* AI-powered lifecycle management for product features.
|
|
5
|
+
* Evaluates phase outputs against quality criteria and determines
|
|
6
|
+
* whether to advance, re-run, or escalate to a human.
|
|
7
|
+
*
|
|
8
|
+
* This module provides:
|
|
9
|
+
* - Type definitions for lifecycle decisions
|
|
10
|
+
* - Quality criteria per phase
|
|
11
|
+
* - Transition rules engine
|
|
12
|
+
* - Core decision-making logic
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import {
|
|
16
|
+
* getPhaseQualityCriteria,
|
|
17
|
+
* determineDecision,
|
|
18
|
+
* buildDecisionResult,
|
|
19
|
+
* DEFAULT_LIFECYCLE_AGENT_CONFIG,
|
|
20
|
+
* } from './services/lifecycle-agent/index.js'
|
|
21
|
+
*/
|
|
22
|
+
export type { LifecycleDecision, CriterionEvaluation, PhaseEvaluationResult, SuggestedFeedback, CriterionDefinition, PhaseQualityCriteria, TransitionRule, LifecycleAgentConfig, LifecycleAgentState, PhaseEvaluationInput, LifecycleDecisionResult, } from './types.js';
|
|
23
|
+
export { DEFAULT_PHASE_CRITERIA, USER_STORIES_ANALYSIS_CRITERIA, TEST_CASES_ANALYSIS_CRITERIA, TECHNICAL_DESIGN_CRITERIA, BRANCH_PLANNING_CRITERIA, CODE_IMPLEMENTATION_CRITERIA, FUNCTIONAL_TESTING_CRITERIA, CODE_REVIEW_CRITERIA, getPhaseQualityCriteria, } from './phase-criteria.js';
|
|
24
|
+
export { DEFAULT_TRANSITION_RULES, DEFAULT_LIFECYCLE_AGENT_CONFIG, getTransitionRule, getRerunExecutionMode, getVerificationStatus, calculateQualityScore, determineDecision, buildDecisionResult, } from './transition-rules.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Agent Module
|
|
3
|
+
*
|
|
4
|
+
* AI-powered lifecycle management for product features.
|
|
5
|
+
* Evaluates phase outputs against quality criteria and determines
|
|
6
|
+
* whether to advance, re-run, or escalate to a human.
|
|
7
|
+
*
|
|
8
|
+
* This module provides:
|
|
9
|
+
* - Type definitions for lifecycle decisions
|
|
10
|
+
* - Quality criteria per phase
|
|
11
|
+
* - Transition rules engine
|
|
12
|
+
* - Core decision-making logic
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import {
|
|
16
|
+
* getPhaseQualityCriteria,
|
|
17
|
+
* determineDecision,
|
|
18
|
+
* buildDecisionResult,
|
|
19
|
+
* DEFAULT_LIFECYCLE_AGENT_CONFIG,
|
|
20
|
+
* } from './services/lifecycle-agent/index.js'
|
|
21
|
+
*/
|
|
22
|
+
// Phase criteria
|
|
23
|
+
export { DEFAULT_PHASE_CRITERIA, USER_STORIES_ANALYSIS_CRITERIA, TEST_CASES_ANALYSIS_CRITERIA, TECHNICAL_DESIGN_CRITERIA, BRANCH_PLANNING_CRITERIA, CODE_IMPLEMENTATION_CRITERIA, FUNCTIONAL_TESTING_CRITERIA, CODE_REVIEW_CRITERIA, getPhaseQualityCriteria, } from './phase-criteria.js';
|
|
24
|
+
// Transition rules and decision logic
|
|
25
|
+
export { DEFAULT_TRANSITION_RULES, DEFAULT_LIFECYCLE_AGENT_CONFIG, getTransitionRule, getRerunExecutionMode, getVerificationStatus, calculateQualityScore, determineDecision, buildDecisionResult, } from './transition-rules.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase Quality Criteria Definitions
|
|
3
|
+
*
|
|
4
|
+
* Defines the quality criteria the lifecycle agent uses to evaluate
|
|
5
|
+
* each phase's outputs. These criteria determine whether a phase
|
|
6
|
+
* should advance, be re-run, or be escalated to a human.
|
|
7
|
+
*
|
|
8
|
+
* Each phase has:
|
|
9
|
+
* - An advance threshold: minimum score to auto-advance
|
|
10
|
+
* - An escalate threshold: score below which to escalate to human
|
|
11
|
+
* - A set of weighted criteria for evaluation
|
|
12
|
+
* - A max retry count before escalation
|
|
13
|
+
*/
|
|
14
|
+
import type { PhaseQualityCriteria } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* User Stories Analysis quality criteria
|
|
17
|
+
* Evaluates whether generated user stories adequately capture the feature requirements
|
|
18
|
+
*/
|
|
19
|
+
export declare const USER_STORIES_ANALYSIS_CRITERIA: PhaseQualityCriteria;
|
|
20
|
+
/**
|
|
21
|
+
* Test Cases Analysis quality criteria
|
|
22
|
+
* Evaluates whether generated test cases provide adequate coverage
|
|
23
|
+
*/
|
|
24
|
+
export declare const TEST_CASES_ANALYSIS_CRITERIA: PhaseQualityCriteria;
|
|
25
|
+
/**
|
|
26
|
+
* Technical Design quality criteria
|
|
27
|
+
* Evaluates the technical design document quality
|
|
28
|
+
*/
|
|
29
|
+
export declare const TECHNICAL_DESIGN_CRITERIA: PhaseQualityCriteria;
|
|
30
|
+
/**
|
|
31
|
+
* Branch Planning quality criteria
|
|
32
|
+
* Evaluates whether the feature is well-split into branches
|
|
33
|
+
*/
|
|
34
|
+
export declare const BRANCH_PLANNING_CRITERIA: PhaseQualityCriteria;
|
|
35
|
+
/**
|
|
36
|
+
* Code Implementation quality criteria
|
|
37
|
+
* Evaluates the code output after implementation
|
|
38
|
+
*/
|
|
39
|
+
export declare const CODE_IMPLEMENTATION_CRITERIA: PhaseQualityCriteria;
|
|
40
|
+
/**
|
|
41
|
+
* Functional Testing quality criteria
|
|
42
|
+
* Evaluates test execution results
|
|
43
|
+
*/
|
|
44
|
+
export declare const FUNCTIONAL_TESTING_CRITERIA: PhaseQualityCriteria;
|
|
45
|
+
/**
|
|
46
|
+
* Code Review quality criteria
|
|
47
|
+
* Evaluates the AI code review output
|
|
48
|
+
*/
|
|
49
|
+
export declare const CODE_REVIEW_CRITERIA: PhaseQualityCriteria;
|
|
50
|
+
/**
|
|
51
|
+
* All default phase criteria indexed by phase name
|
|
52
|
+
*/
|
|
53
|
+
export declare const DEFAULT_PHASE_CRITERIA: Readonly<Record<string, PhaseQualityCriteria>>;
|
|
54
|
+
/**
|
|
55
|
+
* Get quality criteria for a phase, with optional overrides from config
|
|
56
|
+
*/
|
|
57
|
+
export declare function getPhaseQualityCriteria(phase: string, overrides?: Partial<Record<string, Partial<PhaseQualityCriteria>>>): PhaseQualityCriteria | null;
|