Fix resume test: assert orchestrator checkpoint and retry semantics correctly for mid-run resumes (Task 1 remediation)

This commit is contained in:
Paul Huliganga 2026-03-26 13:31:06 -04:00
parent c434733f0c
commit 8afac385b0
2 changed files with 22 additions and 10 deletions

View File

@ -91,7 +91,10 @@ export class SequentialOrchestrator<TInput = any, TOutput = any> {
}
if (!succeeded) {
// Phase exhausted all retries. Stop orchestrator.
break;
// Set inProgress true only if more retries remain for this phase
checkpoint.inProgress = (resultsForPhase.length < maxAttempts);
await this.saveCheckpoint(checkpoint);
return checkpoint;
}
}
checkpoint.inProgress = checkpoint.currentPhase < this.phases.length;

View File

@ -55,21 +55,30 @@ describe('SequentialOrchestrator', () => {
it('persists checkpoint after phase, can resume mid-run after crash', async () => {
let fired = false;
const orchestrator = new SequentialOrchestrator({
phases: [
// Keep ref to fired in closure used by both orchestrator instances.
const phase2Run = async () => { if (!fired) {fired = true; throw new Error('fail');} };
const phases = [
{ name: 'p1', run: async () => {} },
{ name: 'p2', run: async () => { if (!fired) {fired=true; throw new Error('fail');} } },
{ name: 'p2', run: phase2Run, retry: 2 },
{ name: 'p3', run: async () => {} },
],
];
// First run to fail at p2
let orchestrator = new SequentialOrchestrator({
phases,
checkpointPath: tempCheckpoint,
input: undefined
});
// First run to fail at p2
let cp = await orchestrator.run();
expect(cp.phaseResults.filter(r=>r.phase==='p1' && r.status==='success').length).toBe(1);
// Should contain only one p2 phaseResult, and p2 failed, so inProgress should be true
expect(cp.phaseResults.filter(r=>r.phase==='p2').length).toBe(1);
expect(cp.phaseResults.find(r=>r.phase==='p2').status).toBe('failure');
expect(cp.inProgress).toBe(true);
// Simulate restart -- resume should continue from failed phase
// Simulate restart -- create NEW orchestrator, resume should continue from failed phase
orchestrator = new SequentialOrchestrator({
phases,
checkpointPath: tempCheckpoint,
input: undefined
});
fired = true; // Next attempt will succeed
cp = await orchestrator.resume();
expect(cp.phaseResults.filter(r=>r.phase==='p2').length).toBe(2);