// engine/sdd-flow/pipeline.ts — SPEC-583: Six-stage SDD flow state machine
import { resolveModelRouting, resolveTeammateModel, estimateCost } from './model-router.js';
import { evaluateGate } from './gates.js';
const STAGE_ORDER = [
    'planner',
    'reviewer',
    'executor',
    'validator',
    'merger',
    'releaser',
];
function buildWavePlan(specIds, routing, maxTeammates, safetyWaves, overrides) {
    let rawWaves;
    if (safetyWaves) {
        rawWaves = safetyWaves;
    }
    else {
        // chunk specIds into batches of maxTeammates
        rawWaves = [];
        for (let i = 0; i < specIds.length; i += maxTeammates) {
            rawWaves.push({ specIds: specIds.slice(i, i + maxTeammates) });
        }
    }
    const planWaves = rawWaves.map((w, idx) => ({
        index: idx,
        specIds: w.specIds,
        teammates: w.specIds.map((id) => ({
            specId: id,
            model: resolveTeammateModel(id, routing.executor, overrides),
        })),
    }));
    const estimatedCost = estimateCost(specIds, routing, overrides);
    return { waves: planWaves, totalTeammates: specIds.length, estimatedCost };
}
function advanceStage(state, stageName, outputs) {
    const stageIdx = STAGE_ORDER.indexOf(stageName);
    const updatedStages = state.stages.map((s, i) => {
        if (s.name === stageName) {
            return { ...s, status: 'completed', completedAt: new Date().toISOString(), outputs };
        }
        if (i === stageIdx + 1) {
            return { ...s, status: 'in-progress', startedAt: new Date().toISOString() };
        }
        return s;
    });
    return { ...state, stages: updatedStages, currentStage: stageIdx + 1 };
}
function markStageFailed(state, stageName) {
    const updatedStages = state.stages.map((s) => s.name === stageName
        ? { ...s, status: 'failed', completedAt: new Date().toISOString() }
        : s);
    return { ...state, stages: updatedStages };
}
export function runPlannerStage(input) {
    const { state, perTeammateOverrides, maxTeammates = 4, parallelSafetyResult } = input;
    const routing = resolveModelRouting();
    const wavePlan = buildWavePlan(state.specIds, routing, maxTeammates, parallelSafetyResult?.waves, perTeammateOverrides);
    const plannerOutput = {
        wavePlan,
        modelRouting: routing,
        perTeammateModels: Object.fromEntries(state.specIds.map((id) => [
            id,
            resolveTeammateModel(id, routing.executor, perTeammateOverrides),
        ])),
    };
    const newState = advanceStage(state, 'planner', plannerOutput);
    const gateResult = evaluateGate({
        stage: 'planner',
        controlMode: state.controlMode,
        wavePlan,
        modelRouting: routing,
        specIds: state.specIds,
    });
    return { state: newState, output: plannerOutput, gateResult };
}
export function runReviewerStage(state) {
    const reviewerOutput = { reviewedSpecs: state.specIds, allPassed: true };
    const newState = advanceStage(state, 'reviewer', reviewerOutput);
    const gateResult = evaluateGate({
        stage: 'reviewer',
        controlMode: state.controlMode,
        specIds: state.specIds,
    });
    return { state: newState, gateResult };
}
export function runExecutorStage(state, wavePlan) {
    const teammates = wavePlan.waves.flatMap((w) => w.teammates.map((t) => ({
        specId: t.specId,
        model: t.model,
        worktree: `.claude/worktrees/${t.specId.toLowerCase()}`,
        status: 'completed',
    })));
    const stageWithTeammates = state.stages.map((s) => s.name === 'executor' ? { ...s, teammates } : s);
    const stateWithTeammates = { ...state, stages: stageWithTeammates };
    const newState = advanceStage(stateWithTeammates, 'executor', { teammates });
    const gateResult = evaluateGate({
        stage: 'executor',
        controlMode: state.controlMode,
        teammates,
    });
    return { state: newState, teammates, gateResult };
}
export function runValidatorStage(state, teammates) {
    const failed = teammates.filter((t) => t.status === 'failed').map((t) => t.specId);
    const passed = teammates.filter((t) => t.status !== 'failed').map((t) => t.specId);
    const output = { passed, failed, rolledBack: failed };
    const stateAfter = failed.length > 0
        ? markStageFailed(state, 'validator')
        : advanceStage(state, 'validator', output);
    const gateResult = evaluateGate({
        stage: 'validator',
        controlMode: state.controlMode,
        validationFailed: failed,
    });
    return { state: stateAfter, output, gateResult, rollbackIds: failed };
}
export function runMergerStage(state, specIdsToMerge) {
    const mergerOutput = { merged: specIdsToMerge, target: 'develop', strategy: 'ff-only' };
    const newState = advanceStage(state, 'merger', mergerOutput);
    const gateResult = evaluateGate({
        stage: 'merger',
        controlMode: state.controlMode,
        specIds: specIdsToMerge,
    });
    return { state: newState, merged: specIdsToMerge, gateResult };
}
export function runReleaserStage(state) {
    const releaserOutput = { changelogUpdated: true, versionBumped: true };
    const newState = advanceStage(state, 'releaser', releaserOutput);
    const gateResult = evaluateGate({ stage: 'releaser', controlMode: state.controlMode });
    return { state: newState, released: true, gateResult };
}
function handlePause(state, gateResult) {
    const updatedState = { ...state, pendingQuestions: gateResult.questions };
    return {
        state: updatedState,
        status: 'paused',
        interactiveQuestions: gateResult.questions,
    };
}
export function runPipeline(input) {
    // PLANNER
    const plannerResult = runPlannerStage(input);
    const afterPlanner = plannerResult.state;
    if (afterPlanner.controlMode === 'preview' || plannerResult.gateResult.action === 'exit') {
        return { state: afterPlanner, status: 'preview', wavePlan: plannerResult.output.wavePlan };
    }
    if (plannerResult.gateResult.action === 'pause') {
        const pauseResult = handlePause(afterPlanner, plannerResult.gateResult);
        return { ...pauseResult, wavePlan: plannerResult.output.wavePlan };
    }
    // REVIEWER
    const reviewerResult = runReviewerStage(afterPlanner);
    const afterReviewer = reviewerResult.state;
    if (reviewerResult.gateResult.action === 'pause') {
        return handlePause(afterReviewer, reviewerResult.gateResult);
    }
    // EXECUTOR
    const executorResult = runExecutorStage(afterReviewer, plannerResult.output.wavePlan);
    const afterExecutor = executorResult.state;
    if (executorResult.gateResult.action === 'pause') {
        return handlePause(afterExecutor, executorResult.gateResult);
    }
    // VALIDATOR
    const validatorResult = runValidatorStage(afterExecutor, executorResult.teammates);
    const afterValidator = validatorResult.state;
    if (validatorResult.gateResult.action === 'pause') {
        return handlePause(afterValidator, validatorResult.gateResult);
    }
    const specIdsToMerge = validatorResult.output.passed;
    // MERGER
    const mergerResult = runMergerStage(afterValidator, specIdsToMerge);
    const afterMerger = mergerResult.state;
    if (mergerResult.gateResult.action === 'pause') {
        const pauseResult = handlePause(afterMerger, mergerResult.gateResult);
        return { ...pauseResult, merged: mergerResult.merged };
    }
    // RELEASER
    const releaserResult = runReleaserStage(afterMerger);
    if (releaserResult.gateResult.action === 'pause') {
        const pauseResult = handlePause(releaserResult.state, releaserResult.gateResult);
        return { ...pauseResult, merged: mergerResult.merged };
    }
    return {
        state: releaserResult.state,
        status: 'completed',
        merged: mergerResult.merged,
        released: releaserResult.released,
    };
}
export { STAGE_ORDER };
//# sourceMappingURL=pipeline.js.map