edsger 0.19.18 → 0.20.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.
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { FeatureInfo } from '../../types/features.js';
|
|
2
|
+
/**
|
|
3
|
+
* Claim the next available ready_for_ai feature for processing.
|
|
4
|
+
* Uses database-level locking (FOR UPDATE SKIP LOCKED) to prevent
|
|
5
|
+
* race conditions between multiple workers.
|
|
6
|
+
*
|
|
7
|
+
* @param productId - The product ID to claim a feature from
|
|
8
|
+
* @param verbose - Whether to log verbose output
|
|
9
|
+
* @returns The claimed feature or null if no features available
|
|
10
|
+
*/
|
|
11
|
+
export declare function claimNextFeature(productId: string, verbose?: boolean): Promise<FeatureInfo | null>;
|
|
2
12
|
/**
|
|
3
13
|
* Filter features by status
|
|
4
14
|
*/
|
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
2
|
import { callMcpEndpoint } from '../mcp-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Claim the next available ready_for_ai feature for processing.
|
|
5
|
+
* Uses database-level locking (FOR UPDATE SKIP LOCKED) to prevent
|
|
6
|
+
* race conditions between multiple workers.
|
|
7
|
+
*
|
|
8
|
+
* @param productId - The product ID to claim a feature from
|
|
9
|
+
* @param verbose - Whether to log verbose output
|
|
10
|
+
* @returns The claimed feature or null if no features available
|
|
11
|
+
*/
|
|
12
|
+
export async function claimNextFeature(productId, verbose) {
|
|
13
|
+
if (verbose) {
|
|
14
|
+
logInfo(`Attempting to claim next ready_for_ai feature for product: ${productId}`);
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const result = (await callMcpEndpoint('features/claim', {
|
|
18
|
+
product_id: productId,
|
|
19
|
+
}));
|
|
20
|
+
if (result.feature) {
|
|
21
|
+
if (verbose) {
|
|
22
|
+
logInfo(`✅ Claimed feature: ${result.feature.name} (${result.feature.id})`);
|
|
23
|
+
}
|
|
24
|
+
return result.feature;
|
|
25
|
+
}
|
|
26
|
+
if (verbose) {
|
|
27
|
+
logInfo('No features available for processing');
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
33
|
+
logError(`Failed to claim feature: ${errorMessage}`);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
3
37
|
/**
|
|
4
38
|
* Filter features by status
|
|
5
39
|
*/
|
|
@@ -32,7 +32,8 @@ export declare class WorkflowProcessor {
|
|
|
32
32
|
*/
|
|
33
33
|
stop(): void;
|
|
34
34
|
/**
|
|
35
|
-
* Process the next available feature using
|
|
35
|
+
* Process the next available feature using atomic claim mechanism
|
|
36
|
+
* Uses database-level locking to prevent race conditions between workers
|
|
36
37
|
*/
|
|
37
38
|
private processNextFeature;
|
|
38
39
|
/**
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
* Monitors for ready_for_ai features and processes them through the complete pipeline
|
|
4
4
|
* Uses functional programming principles
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { claimNextFeature } from '../../api/features/index.js';
|
|
7
7
|
import { runFeatureWorkflow } from './feature-coordinator.js';
|
|
8
8
|
import { logInfo } from '../../utils/logger.js';
|
|
9
9
|
// Import core modules
|
|
10
10
|
import { createInitialState, updateFeatureState, createProcessingState, createCompletedState, createFailedState, calculateStats, } from './core/state-manager.js';
|
|
11
|
-
import { findNextFeature } from './core/feature-filter.js';
|
|
12
11
|
import { evaluatePipelineResults } from './core/pipeline-evaluator.js';
|
|
13
|
-
import { logProcessingStart, logRetryInfo, logPipelineResults, logProcessorStart, logProcessorReady, logProcessorStop, logFeatureSuccess, logFeatureFailed, logFeatureError, logNoFeaturesFound,
|
|
12
|
+
import { logProcessingStart, logRetryInfo, logPipelineResults, logProcessorStart, logProcessorReady, logProcessorStop, logFeatureSuccess, logFeatureFailed, logFeatureError, logNoFeaturesFound, logSkippingProcessing, logPollingError, logProcessNextFeatureError, } from './core/workflow-logger.js';
|
|
14
13
|
/**
|
|
15
14
|
* Workflow processor using functional programming principles
|
|
16
15
|
*/
|
|
@@ -64,7 +63,8 @@ export class WorkflowProcessor {
|
|
|
64
63
|
logProcessorStop();
|
|
65
64
|
}
|
|
66
65
|
/**
|
|
67
|
-
* Process the next available feature using
|
|
66
|
+
* Process the next available feature using atomic claim mechanism
|
|
67
|
+
* Uses database-level locking to prevent race conditions between workers
|
|
68
68
|
*/
|
|
69
69
|
async processNextFeature() {
|
|
70
70
|
try {
|
|
@@ -76,24 +76,17 @@ export class WorkflowProcessor {
|
|
|
76
76
|
}
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
// Atomically claim the next available feature
|
|
80
|
+
// This uses FOR UPDATE SKIP LOCKED to prevent multiple workers from claiming the same feature
|
|
81
|
+
const claimedFeature = await claimNextFeature(this.options.productId, this.options.verbose);
|
|
82
|
+
if (!claimedFeature) {
|
|
82
83
|
if (this.options.verbose) {
|
|
83
84
|
logNoFeaturesFound();
|
|
84
85
|
}
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
if (!nextFeature) {
|
|
90
|
-
if (this.options.verbose) {
|
|
91
|
-
logAllFeaturesProcessed();
|
|
92
|
-
}
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
// Process the feature
|
|
96
|
-
await this.processFeature(nextFeature);
|
|
88
|
+
// Process the claimed feature
|
|
89
|
+
await this.processFeature(claimedFeature);
|
|
97
90
|
}
|
|
98
91
|
catch (error) {
|
|
99
92
|
logProcessNextFeatureError(error);
|