Parent: #126
What
Replace 14+ direct handlers.startX() cross-calls with a return-value dispatch pattern. Each phase handler returns a typed outcome instead of calling the next phase:
type PhaseOutcome =
| { next: 'review'; plan: ShipCodePlan }
| { next: 'execute'; plan: ShipCodePlan }
| { next: 'testing' }
| { next: 'verification' }
| { next: 'commit' }
| { next: 'shipping' }
| { next: 'plan'; prompt: string }
| { next: 'failed'; error: string }
| { next: 'awaiting_approval' }
| { next: 'retry'; delayMs: number; target: PhaseOutcome['next'] };
Orchestrator loop receives the outcome and dispatches the next phase. Explicit state machine replaces implicit call graph.
Scope
packages/pipeline/src/pipeline/shared.ts — PhaseOutcome type, remove PipelinePhaseHandlers
packages/pipeline/src/pipeline/planning-phases.ts — return outcomes instead of calling handlers
packages/pipeline/src/pipeline/execution-phases.ts — return outcomes instead of calling handlers
packages/pipeline/src/pipeline.ts — new dispatch loop
Estimate
~400 lines. Highest risk item — restructures control flow across the entire pipeline. Needs comprehensive test coverage before and after.
Depends on
#131 (split context) should land first so the dispatch loop operates on clean types.
Parent: #126
What
Replace 14+ direct
handlers.startX()cross-calls with a return-value dispatch pattern. Each phase handler returns a typed outcome instead of calling the next phase:Orchestrator loop receives the outcome and dispatches the next phase. Explicit state machine replaces implicit call graph.
Scope
packages/pipeline/src/pipeline/shared.ts—PhaseOutcometype, removePipelinePhaseHandlerspackages/pipeline/src/pipeline/planning-phases.ts— return outcomes instead of calling handlerspackages/pipeline/src/pipeline/execution-phases.ts— return outcomes instead of calling handlerspackages/pipeline/src/pipeline.ts— new dispatch loopEstimate
~400 lines. Highest risk item — restructures control flow across the entire pipeline. Needs comprehensive test coverage before and after.
Depends on
#131 (split context) should land first so the dispatch loop operates on clean types.