@wundam/orchex 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +65 -0
- package/README.md +332 -0
- package/bin/orchex.js +2 -0
- package/dist/artifacts.d.ts +132 -0
- package/dist/artifacts.js +832 -0
- package/dist/claude-executor.d.ts +31 -0
- package/dist/claude-executor.js +200 -0
- package/dist/commands.d.ts +36 -0
- package/dist/commands.js +264 -0
- package/dist/config.d.ts +100 -0
- package/dist/config.js +172 -0
- package/dist/context-builder.d.ts +46 -0
- package/dist/context-builder.js +506 -0
- package/dist/cost.d.ts +29 -0
- package/dist/cost.js +60 -0
- package/dist/execution-broadcaster.d.ts +18 -0
- package/dist/execution-broadcaster.js +17 -0
- package/dist/executors/base.d.ts +99 -0
- package/dist/executors/base.js +206 -0
- package/dist/executors/circuit-breaker.d.ts +36 -0
- package/dist/executors/circuit-breaker.js +109 -0
- package/dist/executors/deepseek-executor.d.ts +22 -0
- package/dist/executors/deepseek-executor.js +145 -0
- package/dist/executors/gemini-executor.d.ts +20 -0
- package/dist/executors/gemini-executor.js +176 -0
- package/dist/executors/index.d.ts +81 -0
- package/dist/executors/index.js +193 -0
- package/dist/executors/ollama-executor.d.ts +25 -0
- package/dist/executors/ollama-executor.js +184 -0
- package/dist/executors/openai-executor.d.ts +22 -0
- package/dist/executors/openai-executor.js +142 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +115 -0
- package/dist/intelligence/anti-pattern-detector.d.ts +117 -0
- package/dist/intelligence/anti-pattern-detector.js +327 -0
- package/dist/intelligence/budget-enforcer.d.ts +119 -0
- package/dist/intelligence/budget-enforcer.js +226 -0
- package/dist/intelligence/context-optimizer.d.ts +111 -0
- package/dist/intelligence/context-optimizer.js +282 -0
- package/dist/intelligence/cost-tracker.d.ts +114 -0
- package/dist/intelligence/cost-tracker.js +183 -0
- package/dist/intelligence/deliverable-extractor.d.ts +134 -0
- package/dist/intelligence/deliverable-extractor.js +909 -0
- package/dist/intelligence/dependency-inferrer.d.ts +87 -0
- package/dist/intelligence/dependency-inferrer.js +403 -0
- package/dist/intelligence/diagnostics.d.ts +25 -0
- package/dist/intelligence/diagnostics.js +36 -0
- package/dist/intelligence/error-analyzer.d.ts +7 -0
- package/dist/intelligence/error-analyzer.js +76 -0
- package/dist/intelligence/file-chunker.d.ts +15 -0
- package/dist/intelligence/file-chunker.js +64 -0
- package/dist/intelligence/fix-stream-manager.d.ts +59 -0
- package/dist/intelligence/fix-stream-manager.js +212 -0
- package/dist/intelligence/heuristics.d.ts +23 -0
- package/dist/intelligence/heuristics.js +124 -0
- package/dist/intelligence/learning-engine.d.ts +157 -0
- package/dist/intelligence/learning-engine.js +433 -0
- package/dist/intelligence/learning-feedback.d.ts +96 -0
- package/dist/intelligence/learning-feedback.js +202 -0
- package/dist/intelligence/pattern-analyzer.d.ts +35 -0
- package/dist/intelligence/pattern-analyzer.js +189 -0
- package/dist/intelligence/plan-parser.d.ts +124 -0
- package/dist/intelligence/plan-parser.js +498 -0
- package/dist/intelligence/planner.d.ts +29 -0
- package/dist/intelligence/planner.js +86 -0
- package/dist/intelligence/self-healer.d.ts +16 -0
- package/dist/intelligence/self-healer.js +84 -0
- package/dist/intelligence/slicing-metrics.d.ts +62 -0
- package/dist/intelligence/slicing-metrics.js +202 -0
- package/dist/intelligence/slicing-templates.d.ts +81 -0
- package/dist/intelligence/slicing-templates.js +420 -0
- package/dist/intelligence/split-suggester.d.ts +69 -0
- package/dist/intelligence/split-suggester.js +176 -0
- package/dist/intelligence/stream-generator.d.ts +90 -0
- package/dist/intelligence/stream-generator.js +452 -0
- package/dist/logger.d.ts +34 -0
- package/dist/logger.js +83 -0
- package/dist/logging.d.ts +5 -0
- package/dist/logging.js +38 -0
- package/dist/manifest.d.ts +56 -0
- package/dist/manifest.js +254 -0
- package/dist/metrics.d.ts +35 -0
- package/dist/metrics.js +75 -0
- package/dist/orchestrator.d.ts +35 -0
- package/dist/orchestrator.js +723 -0
- package/dist/ownership.d.ts +44 -0
- package/dist/ownership.js +250 -0
- package/dist/semaphore.d.ts +12 -0
- package/dist/semaphore.js +34 -0
- package/dist/telemetry/telemetry-types.d.ts +85 -0
- package/dist/telemetry/telemetry-types.js +1 -0
- package/dist/tier-gating.d.ts +24 -0
- package/dist/tier-gating.js +88 -0
- package/dist/tiers.d.ts +92 -0
- package/dist/tiers.js +108 -0
- package/dist/tools.d.ts +18 -0
- package/dist/tools.js +1363 -0
- package/dist/types.d.ts +740 -0
- package/dist/types.js +160 -0
- package/dist/utils/ownership-validator.d.ts +6 -0
- package/dist/utils/ownership-validator.js +21 -0
- package/dist/waves.d.ts +21 -0
- package/dist/waves.js +146 -0
- package/package.json +120 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learning feedback module for adaptive heuristic adjustment.
|
|
3
|
+
* Analyzes execution history to improve slicing thresholds.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Default heuristic thresholds (conservative starting point).
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_THRESHOLDS = {
|
|
9
|
+
maxOwnsCount: 4,
|
|
10
|
+
maxReadsCount: 4,
|
|
11
|
+
maxPlanAndCount: 3,
|
|
12
|
+
maxOutputTokens: 5000,
|
|
13
|
+
minSuccessRate: 0.7,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Minimum samples required before learning from a category.
|
|
17
|
+
*/
|
|
18
|
+
export const MIN_CATEGORY_SAMPLES = 5;
|
|
19
|
+
/**
|
|
20
|
+
* Minimum total samples before suggesting adjustments.
|
|
21
|
+
*/
|
|
22
|
+
export const MIN_TOTAL_SAMPLES = 20;
|
|
23
|
+
/**
|
|
24
|
+
* Learn from execution metrics and suggest threshold adjustments.
|
|
25
|
+
*
|
|
26
|
+
* @param metrics - Map of category to execution metrics
|
|
27
|
+
* @param currentThresholds - Current threshold values
|
|
28
|
+
* @param minSamples - Minimum samples required for learning
|
|
29
|
+
* @returns Array of suggested threshold adjustments
|
|
30
|
+
*/
|
|
31
|
+
export function learnFromMetrics(metrics, currentThresholds = DEFAULT_THRESHOLDS, minSamples = MIN_TOTAL_SAMPLES) {
|
|
32
|
+
const adjustments = [];
|
|
33
|
+
// Aggregate all metrics
|
|
34
|
+
let totalExecutions = 0;
|
|
35
|
+
let totalSuccess = 0;
|
|
36
|
+
let weightedOwns = 0;
|
|
37
|
+
let weightedReads = 0;
|
|
38
|
+
let categoriesWithData = 0;
|
|
39
|
+
for (const m of metrics.values()) {
|
|
40
|
+
if (m.totalExecutions < MIN_CATEGORY_SAMPLES)
|
|
41
|
+
continue;
|
|
42
|
+
totalExecutions += m.totalExecutions;
|
|
43
|
+
totalSuccess += m.successCount;
|
|
44
|
+
weightedOwns += m.avgOwnsCount * m.totalExecutions;
|
|
45
|
+
weightedReads += m.avgReadsCount * m.totalExecutions;
|
|
46
|
+
categoriesWithData++;
|
|
47
|
+
}
|
|
48
|
+
if (totalExecutions < minSamples) {
|
|
49
|
+
return []; // Not enough data to learn from
|
|
50
|
+
}
|
|
51
|
+
const overallSuccessRate = totalSuccess / totalExecutions;
|
|
52
|
+
const avgOwns = weightedOwns / totalExecutions;
|
|
53
|
+
const avgReads = weightedReads / totalExecutions;
|
|
54
|
+
const confidence = totalExecutions >= 100 ? 'high' :
|
|
55
|
+
totalExecutions >= 50 ? 'medium' : 'low';
|
|
56
|
+
// Analyze high-owns categories with low success
|
|
57
|
+
const highOwnsLowSuccess = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
|
|
58
|
+
m.avgOwnsCount > currentThresholds.maxOwnsCount &&
|
|
59
|
+
m.successRate < 0.6);
|
|
60
|
+
if (highOwnsLowSuccess.length > 0) {
|
|
61
|
+
const newThreshold = Math.max(2, Math.floor(avgOwns * 0.8));
|
|
62
|
+
if (newThreshold !== currentThresholds.maxOwnsCount) {
|
|
63
|
+
adjustments.push({
|
|
64
|
+
field: 'maxOwnsCount',
|
|
65
|
+
oldValue: currentThresholds.maxOwnsCount,
|
|
66
|
+
newValue: newThreshold,
|
|
67
|
+
reason: `Streams with >${currentThresholds.maxOwnsCount} owns have <60% success rate`,
|
|
68
|
+
confidence,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Analyze high-reads categories with high timeout rate
|
|
73
|
+
const highReadsHighTimeout = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
|
|
74
|
+
m.avgReadsCount > currentThresholds.maxReadsCount &&
|
|
75
|
+
(m.timeoutCount / m.totalExecutions) > 0.15);
|
|
76
|
+
if (highReadsHighTimeout.length > 0) {
|
|
77
|
+
const newThreshold = Math.max(2, Math.floor(avgReads * 0.7));
|
|
78
|
+
if (newThreshold !== currentThresholds.maxReadsCount) {
|
|
79
|
+
adjustments.push({
|
|
80
|
+
field: 'maxReadsCount',
|
|
81
|
+
oldValue: currentThresholds.maxReadsCount,
|
|
82
|
+
newValue: newThreshold,
|
|
83
|
+
reason: `Streams with >${currentThresholds.maxReadsCount} reads have >15% timeout rate`,
|
|
84
|
+
confidence,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Adjust minSuccessRate if overall success is consistently high
|
|
89
|
+
if (overallSuccessRate > 0.85 && currentThresholds.minSuccessRate < 0.8) {
|
|
90
|
+
adjustments.push({
|
|
91
|
+
field: 'minSuccessRate',
|
|
92
|
+
oldValue: currentThresholds.minSuccessRate,
|
|
93
|
+
newValue: 0.8,
|
|
94
|
+
reason: `Overall success rate is ${(overallSuccessRate * 100).toFixed(0)}%, raising minimum threshold`,
|
|
95
|
+
confidence,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Adjust minSuccessRate down if overall success is struggling
|
|
99
|
+
if (overallSuccessRate < 0.6 && currentThresholds.minSuccessRate > 0.6) {
|
|
100
|
+
adjustments.push({
|
|
101
|
+
field: 'minSuccessRate',
|
|
102
|
+
oldValue: currentThresholds.minSuccessRate,
|
|
103
|
+
newValue: 0.6,
|
|
104
|
+
reason: `Overall success rate is ${(overallSuccessRate * 100).toFixed(0)}%, lowering minimum threshold temporarily`,
|
|
105
|
+
confidence,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Analyze high output token categories
|
|
109
|
+
const highTokensLowSuccess = [...metrics.values()].filter(m => m.totalExecutions >= MIN_CATEGORY_SAMPLES &&
|
|
110
|
+
m.avgOutputTokens > currentThresholds.maxOutputTokens &&
|
|
111
|
+
m.successRate < 0.7);
|
|
112
|
+
if (highTokensLowSuccess.length > 0) {
|
|
113
|
+
// Calculate average across high-token categories
|
|
114
|
+
const avgHighTokens = highTokensLowSuccess.reduce((sum, m) => sum + m.avgOutputTokens, 0) / highTokensLowSuccess.length;
|
|
115
|
+
const newThreshold = Math.max(3000, Math.floor(avgHighTokens * 0.8));
|
|
116
|
+
if (newThreshold !== currentThresholds.maxOutputTokens) {
|
|
117
|
+
adjustments.push({
|
|
118
|
+
field: 'maxOutputTokens',
|
|
119
|
+
oldValue: currentThresholds.maxOutputTokens,
|
|
120
|
+
newValue: newThreshold,
|
|
121
|
+
reason: `High-token streams averaging ${avgHighTokens.toFixed(0)} tokens have <70% success rate`,
|
|
122
|
+
confidence,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return adjustments;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Apply adjustments to thresholds with learning rate control.
|
|
130
|
+
*
|
|
131
|
+
* @param currentThresholds - Current threshold values
|
|
132
|
+
* @param adjustments - Suggested adjustments
|
|
133
|
+
* @param learningRate - How much to move toward suggested values (0-1)
|
|
134
|
+
* @returns Updated thresholds
|
|
135
|
+
*/
|
|
136
|
+
export function applyAdjustments(currentThresholds, adjustments, learningRate = 0.3) {
|
|
137
|
+
const updated = { ...currentThresholds };
|
|
138
|
+
for (const adj of adjustments) {
|
|
139
|
+
const current = currentThresholds[adj.field];
|
|
140
|
+
const suggested = adj.newValue;
|
|
141
|
+
// Apply learning rate for gradual adaptation
|
|
142
|
+
const newValue = current * (1 - learningRate) + suggested * learningRate;
|
|
143
|
+
// Round appropriately based on field type
|
|
144
|
+
if (adj.field === 'minSuccessRate') {
|
|
145
|
+
updated[adj.field] = Math.round(newValue * 100) / 100; // 2 decimal places
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
updated[adj.field] = Math.round(newValue);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return updated;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Format adjustments as a human-readable report.
|
|
155
|
+
*/
|
|
156
|
+
export function formatAdjustmentsReport(adjustments) {
|
|
157
|
+
if (adjustments.length === 0) {
|
|
158
|
+
return 'No threshold adjustments suggested. Current thresholds are performing well.';
|
|
159
|
+
}
|
|
160
|
+
const lines = [
|
|
161
|
+
'=== Learning Feedback Report ===',
|
|
162
|
+
'',
|
|
163
|
+
`${adjustments.length} threshold adjustment${adjustments.length > 1 ? 's' : ''} suggested:`,
|
|
164
|
+
'',
|
|
165
|
+
];
|
|
166
|
+
for (const adj of adjustments) {
|
|
167
|
+
const confidenceIcon = adj.confidence === 'high' ? '✅' : adj.confidence === 'medium' ? '⚠️' : '❓';
|
|
168
|
+
lines.push(`${confidenceIcon} ${adj.field}: ${adj.oldValue} → ${adj.newValue}`);
|
|
169
|
+
lines.push(` Reason: ${adj.reason}`);
|
|
170
|
+
lines.push(` Confidence: ${adj.confidence}`);
|
|
171
|
+
lines.push('');
|
|
172
|
+
}
|
|
173
|
+
return lines.join('\n');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Generate insights from category metrics.
|
|
177
|
+
*/
|
|
178
|
+
export function generateCategoryInsights(metrics) {
|
|
179
|
+
const insights = [];
|
|
180
|
+
for (const [category, m] of metrics) {
|
|
181
|
+
if (m.totalExecutions < MIN_CATEGORY_SAMPLES)
|
|
182
|
+
continue;
|
|
183
|
+
// Low success rate warning
|
|
184
|
+
if (m.successRate < 0.6) {
|
|
185
|
+
insights.push(`${category} streams have ${(m.successRate * 100).toFixed(0)}% success rate. Consider reducing scope.`);
|
|
186
|
+
}
|
|
187
|
+
// High timeout rate warning
|
|
188
|
+
const timeoutRate = m.timeoutCount / m.totalExecutions;
|
|
189
|
+
if (timeoutRate > 0.15) {
|
|
190
|
+
insights.push(`${category} streams have ${(timeoutRate * 100).toFixed(0)}% timeout rate. Consider increasing timeout or reducing reads.`);
|
|
191
|
+
}
|
|
192
|
+
// High owns warning
|
|
193
|
+
if (m.avgOwnsCount > 4) {
|
|
194
|
+
insights.push(`${category} streams average ${m.avgOwnsCount.toFixed(1)} owned files. Consider splitting into smaller streams.`);
|
|
195
|
+
}
|
|
196
|
+
// High reads warning
|
|
197
|
+
if (m.avgReadsCount > 5) {
|
|
198
|
+
insights.push(`${category} streams average ${m.avgReadsCount.toFixed(1)} read files. High synthesis complexity may cause timeouts.`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return insights;
|
|
202
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TelemetryAggregate } from '../telemetry/telemetry-types.js';
|
|
2
|
+
import type { ErrorCategory } from './error-analyzer.js';
|
|
3
|
+
export interface RetryPattern {
|
|
4
|
+
category: ErrorCategory;
|
|
5
|
+
totalOccurrences: number;
|
|
6
|
+
retrySuccessRate: number;
|
|
7
|
+
recommendation: 'always_retry' | 'conditional_retry' | 'no_retry';
|
|
8
|
+
}
|
|
9
|
+
export interface DurationPrediction {
|
|
10
|
+
complexityScore: number;
|
|
11
|
+
predictedDurationMs: number;
|
|
12
|
+
confidence: 'high' | 'medium' | 'low';
|
|
13
|
+
}
|
|
14
|
+
export interface WaveSizingRecommendation {
|
|
15
|
+
optimalParallelCount: number;
|
|
16
|
+
rationale: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PatternAnalysis {
|
|
19
|
+
retryPatterns: RetryPattern[];
|
|
20
|
+
durationPredictions: DurationPrediction[];
|
|
21
|
+
waveSizing: WaveSizingRecommendation;
|
|
22
|
+
insights: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Analyze telemetry aggregates to derive patterns.
|
|
26
|
+
* Requires at least 7 days of data for meaningful analysis.
|
|
27
|
+
*/
|
|
28
|
+
export declare function analyzePatterns(aggregates: TelemetryAggregate[]): PatternAnalysis;
|
|
29
|
+
/**
|
|
30
|
+
* Get retry recommendation for a specific error category.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getRetryRecommendation(patterns: RetryPattern[], category: ErrorCategory): {
|
|
33
|
+
shouldRetry: boolean;
|
|
34
|
+
confidence: number;
|
|
35
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyze telemetry aggregates to derive patterns.
|
|
3
|
+
* Requires at least 7 days of data for meaningful analysis.
|
|
4
|
+
*/
|
|
5
|
+
export function analyzePatterns(aggregates) {
|
|
6
|
+
const insights = [];
|
|
7
|
+
if (aggregates.length < 7) {
|
|
8
|
+
insights.push(`Insufficient data: ${aggregates.length} days. Need at least 7 days for pattern analysis.`);
|
|
9
|
+
return {
|
|
10
|
+
retryPatterns: [],
|
|
11
|
+
durationPredictions: [],
|
|
12
|
+
waveSizing: { optimalParallelCount: 5, rationale: 'Default (insufficient data)' },
|
|
13
|
+
insights,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Aggregate totals across all periods
|
|
17
|
+
const totals = aggregates.reduce((acc, agg) => ({
|
|
18
|
+
orchestrationsTotal: acc.orchestrationsTotal + agg.orchestrationsTotal,
|
|
19
|
+
orchestrationsSuccess: acc.orchestrationsSuccess + agg.orchestrationsSuccess,
|
|
20
|
+
streamsTotal: acc.streamsTotal + agg.streamsTotal,
|
|
21
|
+
streamsSuccess: acc.streamsSuccess + agg.streamsSuccess,
|
|
22
|
+
selfHealTriggered: acc.selfHealTriggered + agg.selfHealTriggered,
|
|
23
|
+
selfHealSuccess: acc.selfHealSuccess + agg.selfHealSuccess,
|
|
24
|
+
errorsByCategory: {
|
|
25
|
+
timeout: acc.errorsByCategory.timeout + agg.errorsByCategory.timeout,
|
|
26
|
+
edit_mismatch: acc.errorsByCategory.edit_mismatch + agg.errorsByCategory.edit_mismatch,
|
|
27
|
+
invalid_artifact: acc.errorsByCategory.invalid_artifact + agg.errorsByCategory.invalid_artifact,
|
|
28
|
+
environment: acc.errorsByCategory.environment + agg.errorsByCategory.environment,
|
|
29
|
+
test_failure: acc.errorsByCategory.test_failure + agg.errorsByCategory.test_failure,
|
|
30
|
+
lint_error: acc.errorsByCategory.lint_error + agg.errorsByCategory.lint_error,
|
|
31
|
+
runtime_error: acc.errorsByCategory.runtime_error + agg.errorsByCategory.runtime_error,
|
|
32
|
+
unknown: acc.errorsByCategory.unknown + agg.errorsByCategory.unknown,
|
|
33
|
+
},
|
|
34
|
+
durationSum: acc.durationSum + (agg.avgDurationMs ?? 0),
|
|
35
|
+
durationCount: acc.durationCount + (agg.avgDurationMs ? 1 : 0),
|
|
36
|
+
}), {
|
|
37
|
+
orchestrationsTotal: 0, orchestrationsSuccess: 0, streamsTotal: 0, streamsSuccess: 0,
|
|
38
|
+
selfHealTriggered: 0, selfHealSuccess: 0,
|
|
39
|
+
errorsByCategory: {
|
|
40
|
+
timeout: 0, edit_mismatch: 0, invalid_artifact: 0, environment: 0,
|
|
41
|
+
test_failure: 0, lint_error: 0, runtime_error: 0, unknown: 0,
|
|
42
|
+
},
|
|
43
|
+
durationSum: 0,
|
|
44
|
+
durationCount: 0,
|
|
45
|
+
});
|
|
46
|
+
// Calculate retry patterns
|
|
47
|
+
const retryPatterns = [];
|
|
48
|
+
const selfHealRate = totals.selfHealTriggered > 0
|
|
49
|
+
? totals.selfHealSuccess / totals.selfHealTriggered
|
|
50
|
+
: 0;
|
|
51
|
+
for (const [category, count] of Object.entries(totals.errorsByCategory)) {
|
|
52
|
+
if (count === 0)
|
|
53
|
+
continue;
|
|
54
|
+
// Estimate retry success rate based on error category characteristics
|
|
55
|
+
// These rates are derived from empirical observations and can be tuned
|
|
56
|
+
let retrySuccessRate;
|
|
57
|
+
let recommendation;
|
|
58
|
+
switch (category) {
|
|
59
|
+
case 'timeout':
|
|
60
|
+
retrySuccessRate = 0.7; // Timeouts often succeed on retry
|
|
61
|
+
recommendation = 'always_retry';
|
|
62
|
+
break;
|
|
63
|
+
case 'edit_mismatch':
|
|
64
|
+
retrySuccessRate = 0.9; // Almost always fixable with fresh context
|
|
65
|
+
recommendation = 'always_retry';
|
|
66
|
+
break;
|
|
67
|
+
case 'invalid_artifact':
|
|
68
|
+
retrySuccessRate = 0.6; // Often fixable with clearer instructions
|
|
69
|
+
recommendation = 'conditional_retry';
|
|
70
|
+
break;
|
|
71
|
+
case 'environment':
|
|
72
|
+
retrySuccessRate = 0.1; // Rarely fixable by retry
|
|
73
|
+
recommendation = 'no_retry';
|
|
74
|
+
break;
|
|
75
|
+
case 'test_failure':
|
|
76
|
+
// Self-healing helps significantly with test failures
|
|
77
|
+
retrySuccessRate = selfHealRate > 0.5 ? 0.7 : 0.5;
|
|
78
|
+
recommendation = 'conditional_retry';
|
|
79
|
+
break;
|
|
80
|
+
case 'lint_error':
|
|
81
|
+
retrySuccessRate = 0.8; // Usually fixable
|
|
82
|
+
recommendation = 'always_retry';
|
|
83
|
+
break;
|
|
84
|
+
case 'runtime_error':
|
|
85
|
+
retrySuccessRate = 0.4; // Depends on error type
|
|
86
|
+
recommendation = 'conditional_retry';
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
retrySuccessRate = 0.3;
|
|
90
|
+
recommendation = 'conditional_retry';
|
|
91
|
+
}
|
|
92
|
+
retryPatterns.push({ category, totalOccurrences: count, retrySuccessRate, recommendation });
|
|
93
|
+
}
|
|
94
|
+
// Calculate success rates
|
|
95
|
+
const successRate = totals.orchestrationsTotal > 0
|
|
96
|
+
? totals.orchestrationsSuccess / totals.orchestrationsTotal
|
|
97
|
+
: 0;
|
|
98
|
+
const streamSuccessRate = totals.streamsTotal > 0
|
|
99
|
+
? totals.streamsSuccess / totals.streamsTotal
|
|
100
|
+
: 0;
|
|
101
|
+
// Generate insights
|
|
102
|
+
insights.push(`Overall success rate: ${(successRate * 100).toFixed(1)}% (${totals.orchestrationsSuccess}/${totals.orchestrationsTotal})`);
|
|
103
|
+
insights.push(`Stream success rate: ${(streamSuccessRate * 100).toFixed(1)}% (${totals.streamsSuccess}/${totals.streamsTotal})`);
|
|
104
|
+
if (selfHealRate > 0) {
|
|
105
|
+
insights.push(`Self-healing success rate: ${(selfHealRate * 100).toFixed(1)}% (${totals.selfHealSuccess}/${totals.selfHealTriggered})`);
|
|
106
|
+
}
|
|
107
|
+
// Find most common error
|
|
108
|
+
const topError = Object.entries(totals.errorsByCategory)
|
|
109
|
+
.sort(([, a], [, b]) => b - a)[0];
|
|
110
|
+
if (topError && topError[1] > 0) {
|
|
111
|
+
insights.push(`Most common error: ${topError[0]} (${topError[1]} occurrences)`);
|
|
112
|
+
}
|
|
113
|
+
// Analyze trends
|
|
114
|
+
if (aggregates.length >= 14) {
|
|
115
|
+
const recentWeek = aggregates.slice(-7);
|
|
116
|
+
const previousWeek = aggregates.slice(-14, -7);
|
|
117
|
+
const recentSuccess = recentWeek.reduce((sum, a) => sum + a.orchestrationsSuccess, 0) /
|
|
118
|
+
Math.max(1, recentWeek.reduce((sum, a) => sum + a.orchestrationsTotal, 0));
|
|
119
|
+
const previousSuccess = previousWeek.reduce((sum, a) => sum + a.orchestrationsSuccess, 0) /
|
|
120
|
+
Math.max(1, previousWeek.reduce((sum, a) => sum + a.orchestrationsTotal, 0));
|
|
121
|
+
const trend = recentSuccess - previousSuccess;
|
|
122
|
+
if (Math.abs(trend) > 0.05) {
|
|
123
|
+
insights.push(`Trend: ${trend > 0 ? 'Improving' : 'Declining'} (${(trend * 100).toFixed(1)}% change week-over-week)`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Duration predictions based on actual data
|
|
127
|
+
const avgDuration = totals.durationCount > 0
|
|
128
|
+
? totals.durationSum / totals.durationCount
|
|
129
|
+
: 60000; // Default 60s
|
|
130
|
+
const durationPredictions = [
|
|
131
|
+
{
|
|
132
|
+
complexityScore: 1,
|
|
133
|
+
predictedDurationMs: Math.round(avgDuration * 0.3),
|
|
134
|
+
confidence: totals.durationCount >= 10 ? 'medium' : 'low',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
complexityScore: 5,
|
|
138
|
+
predictedDurationMs: Math.round(avgDuration),
|
|
139
|
+
confidence: totals.durationCount >= 10 ? 'high' : 'medium',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
complexityScore: 10,
|
|
143
|
+
predictedDurationMs: Math.round(avgDuration * 2),
|
|
144
|
+
confidence: totals.durationCount >= 10 ? 'medium' : 'low',
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
// Wave sizing recommendation based on success rate and error patterns
|
|
148
|
+
let optimalParallelCount = 5;
|
|
149
|
+
let rationale = '';
|
|
150
|
+
if (successRate > 0.85) {
|
|
151
|
+
optimalParallelCount = 10;
|
|
152
|
+
rationale = 'High success rate (>85%) supports aggressive parallelization';
|
|
153
|
+
}
|
|
154
|
+
else if (successRate > 0.7) {
|
|
155
|
+
optimalParallelCount = 7;
|
|
156
|
+
rationale = 'Good success rate (>70%) supports moderate parallelization';
|
|
157
|
+
}
|
|
158
|
+
else if (successRate > 0.5) {
|
|
159
|
+
optimalParallelCount = 5;
|
|
160
|
+
rationale = 'Moderate success rate (>50%) suggests conservative parallelization';
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
optimalParallelCount = 3;
|
|
164
|
+
rationale = 'Low success rate (<50%) - recommend limited parallelization for easier debugging';
|
|
165
|
+
}
|
|
166
|
+
// Adjust for timeout patterns (high timeouts suggest reducing parallelism)
|
|
167
|
+
const timeoutRate = totals.orchestrationsTotal > 0
|
|
168
|
+
? totals.errorsByCategory.timeout / totals.orchestrationsTotal
|
|
169
|
+
: 0;
|
|
170
|
+
if (timeoutRate > 0.2 && optimalParallelCount > 5) {
|
|
171
|
+
optimalParallelCount = Math.max(5, optimalParallelCount - 2);
|
|
172
|
+
rationale += '; reduced due to high timeout rate';
|
|
173
|
+
}
|
|
174
|
+
const waveSizing = { optimalParallelCount, rationale };
|
|
175
|
+
return { retryPatterns, durationPredictions, waveSizing, insights };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get retry recommendation for a specific error category.
|
|
179
|
+
*/
|
|
180
|
+
export function getRetryRecommendation(patterns, category) {
|
|
181
|
+
const pattern = patterns.find(p => p.category === category);
|
|
182
|
+
if (!pattern) {
|
|
183
|
+
return { shouldRetry: true, confidence: 0.5 }; // Default to retry with low confidence
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
shouldRetry: pattern.recommendation !== 'no_retry',
|
|
187
|
+
confidence: pattern.retrySuccessRate,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown plan document parser.
|
|
3
|
+
* Extracts structure from planning documents for stream generation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Code block extracted from markdown.
|
|
7
|
+
*/
|
|
8
|
+
export interface CodeBlock {
|
|
9
|
+
/** Programming language (from fence) */
|
|
10
|
+
language: string;
|
|
11
|
+
/** Code content */
|
|
12
|
+
code: string;
|
|
13
|
+
/** Filename if specified in comment */
|
|
14
|
+
filename?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parsed section from markdown document.
|
|
18
|
+
*/
|
|
19
|
+
export interface ParsedSection {
|
|
20
|
+
/** Header level (1=H1, 2=H2, etc.) */
|
|
21
|
+
level: number;
|
|
22
|
+
/** Section title */
|
|
23
|
+
title: string;
|
|
24
|
+
/** Section content (excluding children) */
|
|
25
|
+
content: string;
|
|
26
|
+
/** Code blocks in this section */
|
|
27
|
+
codeBlocks: CodeBlock[];
|
|
28
|
+
/** File references found in content */
|
|
29
|
+
fileReferences: string[];
|
|
30
|
+
/** Explicit dependencies mentioned in text */
|
|
31
|
+
explicitDeps: string[];
|
|
32
|
+
/** Child sections */
|
|
33
|
+
children: ParsedSection[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Fully parsed planning document.
|
|
37
|
+
*/
|
|
38
|
+
export interface ParsedPlan {
|
|
39
|
+
/** Document title (from first H1) */
|
|
40
|
+
title: string;
|
|
41
|
+
/** Brief description */
|
|
42
|
+
description: string;
|
|
43
|
+
/** Top-level sections */
|
|
44
|
+
sections: ParsedSection[];
|
|
45
|
+
/** All file references across document */
|
|
46
|
+
allFileReferences: string[];
|
|
47
|
+
/** All code blocks across document */
|
|
48
|
+
allCodeBlocks: CodeBlock[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract file references from text.
|
|
52
|
+
* Matches patterns like: `src/foo.ts`, "src/bar.js", src/baz.ts
|
|
53
|
+
*
|
|
54
|
+
* Code blocks are stripped first to avoid extracting import paths
|
|
55
|
+
* (e.g., `from './foo.js'`) as real file references.
|
|
56
|
+
*
|
|
57
|
+
* Post-processing: `.js` files are dropped when a corresponding `.ts` exists,
|
|
58
|
+
* since TypeScript projects use .js in imports but .ts as source files.
|
|
59
|
+
*/
|
|
60
|
+
export declare function extractFileReferences(text: string): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Extract explicit dependency mentions from text.
|
|
63
|
+
* Matches patterns like: "depends on X", "requires Y", "after Z"
|
|
64
|
+
*/
|
|
65
|
+
export declare function extractExplicitDeps(text: string): string[];
|
|
66
|
+
/**
|
|
67
|
+
* Extract code blocks from markdown.
|
|
68
|
+
* Only matches fences at column 0 (start of line) to avoid treating
|
|
69
|
+
* indented code examples inside YAML plan fields as separate blocks.
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractCodeBlocks(markdown: string): CodeBlock[];
|
|
72
|
+
/**
|
|
73
|
+
* Parse markdown into hierarchical sections.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseMarkdownSections(markdown: string): ParsedSection[];
|
|
76
|
+
/**
|
|
77
|
+
* Parse a markdown planning document.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parsePlanDocument(markdown: string): ParsedPlan;
|
|
80
|
+
/**
|
|
81
|
+
* Get sections at a specific level (typically H2 for major deliverables).
|
|
82
|
+
*/
|
|
83
|
+
export declare function getSectionsAtLevel(sections: ParsedSection[], level: number): ParsedSection[];
|
|
84
|
+
/**
|
|
85
|
+
* Flatten all sections into a single array.
|
|
86
|
+
*/
|
|
87
|
+
export declare function flattenSections(sections: ParsedSection[]): ParsedSection[];
|
|
88
|
+
/**
|
|
89
|
+
* Parsed YAML stream definition from a code block.
|
|
90
|
+
*/
|
|
91
|
+
export interface YamlStreamDefinition {
|
|
92
|
+
/** Stream ID */
|
|
93
|
+
id: string;
|
|
94
|
+
/** Human-readable name */
|
|
95
|
+
name: string;
|
|
96
|
+
/** Dependencies */
|
|
97
|
+
deps?: string[];
|
|
98
|
+
/** Owned files */
|
|
99
|
+
owns?: string[];
|
|
100
|
+
/** Read files */
|
|
101
|
+
reads?: string[];
|
|
102
|
+
/** Implementation plan */
|
|
103
|
+
plan?: string;
|
|
104
|
+
/** Verification commands */
|
|
105
|
+
verify?: string[];
|
|
106
|
+
/** Setup commands */
|
|
107
|
+
setup?: string[];
|
|
108
|
+
/** Timeout in milliseconds */
|
|
109
|
+
timeoutMs?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extract YAML stream definitions from code blocks in a section.
|
|
113
|
+
* Supports both flat format (one block per stream) and nested `streams:` wrapper format.
|
|
114
|
+
*/
|
|
115
|
+
export declare function extractYamlStreamDefinitions(section: ParsedSection): YamlStreamDefinition[];
|
|
116
|
+
/**
|
|
117
|
+
* Find all sections that contain YAML stream definitions.
|
|
118
|
+
*/
|
|
119
|
+
export declare function findYamlStreamSections(sections: ParsedSection[]): ParsedSection[];
|
|
120
|
+
/**
|
|
121
|
+
* Detect whether content is an unpopulated init-plan template.
|
|
122
|
+
* Checks for the ORCHEX PLAN TEMPLATE marker in HTML comments.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isUnpopulatedTemplate(content: string): boolean;
|