/** * Runs an ordered list of Stage instances sequentially, feeding each * stage's output into the next. Stops early if a stage returns a * "kill" signal (e.g. ScoreStage rejecting a low-potential idea) so * you don't spend build-stage money on bad concepts. */ export class Pipeline { constructor(stages, { onStageComplete } = {}) { this.stages = stages; // array of Stage instances, in order this.onStageComplete = onStageComplete || (() => {}); } async run(seedInput) { let current = seedInput; const trace = []; for (const stage of this.stages) { const result = await stage.run(current); trace.push(result); this.onStageComplete(result); // Convention: a stage can attach { kill: true, reason } to its output // to halt the pipeline early (used by ScoreStage). if (result.output && result.output.kill) { return { killed: true, killedAt: stage.name, reason: result.output.reason, trace }; } current = result.output; } return { killed: false, final: current, trace }; } }