refactor(04-02): wire PipelineOrchestrator into AutoModeService

- Add PipelineOrchestrator constructor parameter and property
- Initialize PipelineOrchestrator with all required dependencies and callbacks
- Delegate executePipelineSteps to pipelineOrchestrator.executePipeline()
- Delegate detectPipelineStatus to pipelineOrchestrator.detectPipelineStatus()
- Delegate resumePipelineFeature to pipelineOrchestrator.resumePipeline()
This commit is contained in:
Shirone
2026-01-27 17:56:11 +01:00
parent 8ab77f6583
commit eaa0312c1e

View File

@@ -85,6 +85,11 @@ import {
import { getNotificationService } from './notification-service.js'; import { getNotificationService } from './notification-service.js';
import { extractSummary } from './spec-parser.js'; import { extractSummary } from './spec-parser.js';
import { AgentExecutor } from './agent-executor.js'; import { AgentExecutor } from './agent-executor.js';
import {
PipelineOrchestrator,
type PipelineStatusInfo as OrchestratorPipelineStatusInfo,
} from './pipeline-orchestrator.js';
import { TestRunnerService } from './test-runner-service.js';
const execAsync = promisify(exec); const execAsync = promisify(exec);
@@ -204,6 +209,7 @@ export class AutoModeService {
private config: AutoModeConfig | null = null; private config: AutoModeConfig | null = null;
private planApprovalService: PlanApprovalService; private planApprovalService: PlanApprovalService;
private agentExecutor: AgentExecutor; private agentExecutor: AgentExecutor;
private pipelineOrchestrator: PipelineOrchestrator;
private settingsService: SettingsService | null = null; private settingsService: SettingsService | null = null;
// Track consecutive failures to detect quota/API issues (legacy global, now per-project in autoLoopsByProject) // Track consecutive failures to detect quota/API issues (legacy global, now per-project in autoLoopsByProject)
private consecutiveFailures: { timestamp: number; error: string }[] = []; private consecutiveFailures: { timestamp: number; error: string }[] = [];
@@ -219,7 +225,8 @@ export class AutoModeService {
worktreeResolver?: WorktreeResolver, worktreeResolver?: WorktreeResolver,
featureStateManager?: FeatureStateManager, featureStateManager?: FeatureStateManager,
planApprovalService?: PlanApprovalService, planApprovalService?: PlanApprovalService,
agentExecutor?: AgentExecutor agentExecutor?: AgentExecutor,
pipelineOrchestrator?: PipelineOrchestrator
) { ) {
this.events = events; this.events = events;
this.eventBus = eventBus ?? new TypedEventBus(events); this.eventBus = eventBus ?? new TypedEventBus(events);
@@ -243,6 +250,36 @@ export class AutoModeService {
this.planApprovalService, this.planApprovalService,
this.settingsService this.settingsService
); );
// PipelineOrchestrator encapsulates pipeline step execution
this.pipelineOrchestrator =
pipelineOrchestrator ??
new PipelineOrchestrator(
this.eventBus,
this.featureStateManager,
this.agentExecutor,
new TestRunnerService(),
this.worktreeResolver,
this.concurrencyManager,
this.settingsService,
// Callbacks wrapping AutoModeService methods
(projectPath, featureId, status) =>
this.updateFeatureStatus(projectPath, featureId, status),
loadContextFiles,
(feature, prompts) => this.buildFeaturePrompt(feature, prompts),
(projectPath, featureId, useWorktrees, useScreenshots, model, options) =>
this.executeFeature(projectPath, featureId, useWorktrees, useScreenshots, model, options),
(workDir, featureId, prompt, abortController, projectPath, imagePaths, model, options) =>
this.runAgent(
workDir,
featureId,
prompt,
abortController,
projectPath,
imagePaths,
model,
options
)
);
} }
/** /**
@@ -1224,16 +1261,20 @@ export class AutoModeService {
.filter((step) => !excludedStepIds.has(step.id)); .filter((step) => !excludedStepIds.has(step.id));
if (sortedSteps.length > 0) { if (sortedSteps.length > 0) {
// Execute pipeline steps sequentially // Execute pipeline steps sequentially via PipelineOrchestrator
await this.executePipelineSteps( await this.pipelineOrchestrator.executePipeline({
projectPath, projectPath,
featureId, featureId,
feature, feature,
sortedSteps, steps: sortedSteps,
workDir, workDir,
worktreePath,
branchName: feature.branchName ?? null,
abortController, abortController,
autoLoadClaudeMd autoLoadClaudeMd,
); testAttempts: 0,
maxTestAttempts: 5,
});
} }
// Determine final status based on testing mode: // Determine final status based on testing mode:
@@ -1569,20 +1610,24 @@ Complete the pipeline step instructions above. Review the previous work and appl
`[AutoMode] Resuming feature ${featureId} (${feature.title}) - current status: ${feature.status}` `[AutoMode] Resuming feature ${featureId} (${feature.title}) - current status: ${feature.status}`
); );
// Check if feature is stuck in a pipeline step // Check if feature is stuck in a pipeline step via PipelineOrchestrator
const pipelineInfo = await this.detectPipelineStatus( const pipelineInfo = await this.pipelineOrchestrator.detectPipelineStatus(
projectPath, projectPath,
featureId, featureId,
(feature.status || '') as FeatureStatusWithPipeline (feature.status || '') as FeatureStatusWithPipeline
); );
if (pipelineInfo.isPipeline) { if (pipelineInfo.isPipeline) {
// Feature stuck in pipeline - use pipeline resume // Feature stuck in pipeline - use pipeline resume via PipelineOrchestrator
// Pass _alreadyTracked to prevent double-tracking
logger.info( logger.info(
`[AutoMode] Feature ${featureId} is in pipeline step ${pipelineInfo.stepId}, using pipeline resume` `[AutoMode] Feature ${featureId} is in pipeline step ${pipelineInfo.stepId}, using pipeline resume`
); );
return await this.resumePipelineFeature(projectPath, feature, useWorktrees, pipelineInfo); return await this.pipelineOrchestrator.resumePipeline(
projectPath,
feature,
useWorktrees,
pipelineInfo
);
} }
// Normal resume flow for non-pipeline features // Normal resume flow for non-pipeline features