agentic-orchestrator 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/control-plane/src/cli/retry-command-handler.ts +18 -3
- package/apps/control-plane/src/supervisor/build-wave-executor.ts +3 -0
- package/apps/control-plane/src/supervisor/planning-wave-executor.ts +167 -148
- package/apps/control-plane/src/supervisor/qa-wave-executor.ts +3 -0
- package/apps/control-plane/test/planning-wave-executor.spec.ts +40 -0
- package/dist/apps/control-plane/cli/retry-command-handler.js +10 -3
- package/dist/apps/control-plane/cli/retry-command-handler.js.map +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js +146 -130
- package/dist/apps/control-plane/supervisor/planning-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js +3 -0
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
- package/package.json +1 -1
|
@@ -34,10 +34,21 @@ interface RetryFeatureStatePayload {
|
|
|
34
34
|
reviewer_message?: string | null;
|
|
35
35
|
exhausted_at?: string | null;
|
|
36
36
|
};
|
|
37
|
+
recovery?: {
|
|
38
|
+
resume_phase?: string;
|
|
39
|
+
};
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
function resolveBlockedRetryStatus(
|
|
43
|
+
function resolveBlockedRetryStatus(
|
|
44
|
+
mode: string,
|
|
45
|
+
resumePhase: string | null,
|
|
46
|
+
): 'planning' | 'building' | 'qa' | 'ready_to_merge' | null {
|
|
47
|
+
// If recovery knows the phase where the failure occurred, resume there instead of
|
|
48
|
+
// inferring from the gate mode (which may not have been reached).
|
|
49
|
+
if (resumePhase === 'planning' || resumePhase === 'intake') {
|
|
50
|
+
return 'planning';
|
|
51
|
+
}
|
|
41
52
|
if (mode === 'fast') {
|
|
42
53
|
return 'building';
|
|
43
54
|
}
|
|
@@ -119,6 +130,9 @@ export class RetryCommandHandler {
|
|
|
119
130
|
}
|
|
120
131
|
const lastGateMode = statePayload.front_matter.evidence?.last_gate_mode;
|
|
121
132
|
const gates = statePayload.front_matter.gates ?? {};
|
|
133
|
+
const recovery = statePayload.front_matter.recovery as { resume_phase?: string } | undefined;
|
|
134
|
+
const recoveryResumePhase =
|
|
135
|
+
typeof recovery?.resume_phase === 'string' ? recovery.resume_phase : null;
|
|
122
136
|
const inferredMode =
|
|
123
137
|
typeof lastGateMode === 'string' && lastGateMode.length > 0
|
|
124
138
|
? lastGateMode
|
|
@@ -215,7 +229,8 @@ export class RetryCommandHandler {
|
|
|
215
229
|
gate_retry_count: 0,
|
|
216
230
|
last_retry_at: null,
|
|
217
231
|
};
|
|
218
|
-
const reroutedStatus =
|
|
232
|
+
const reroutedStatus =
|
|
233
|
+
status === 'blocked' ? resolveBlockedRetryStatus(inferredMode, recoveryResumePhase) : null;
|
|
219
234
|
if (reroutedStatus) {
|
|
220
235
|
frontMatterPatch.status = reroutedStatus;
|
|
221
236
|
frontMatterPatch.status_reason = `forced_retry:${inferredMode}`;
|
|
@@ -278,7 +293,7 @@ export class RetryCommandHandler {
|
|
|
278
293
|
},
|
|
279
294
|
};
|
|
280
295
|
} else if (status === 'blocked') {
|
|
281
|
-
const reroutedStatus = resolveBlockedRetryStatus(inferredMode);
|
|
296
|
+
const reroutedStatus = resolveBlockedRetryStatus(inferredMode, recoveryResumePhase);
|
|
282
297
|
const frontMatterPatch: Record<string, unknown> = {
|
|
283
298
|
gate_retry_count: 0,
|
|
284
299
|
last_retry_at: null,
|
|
@@ -558,6 +558,9 @@ export class BuildWaveExecutor {
|
|
|
558
558
|
if (this.providerMode !== 'live') {
|
|
559
559
|
throw error;
|
|
560
560
|
}
|
|
561
|
+
if ((error as Record<string, unknown>)?.normalizedResponse) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
561
564
|
const typed = error as { code?: string; message?: string };
|
|
562
565
|
const code =
|
|
563
566
|
typeof typed.code === 'string' && typed.code.length > 0
|
|
@@ -593,145 +593,177 @@ export class PlanningWaveExecutor {
|
|
|
593
593
|
async runPostQaReconciliation(featureIds: string[], iteration: number): Promise<void> {
|
|
594
594
|
await this.plannerSessionSync?.syncPlannerSessions(featureIds);
|
|
595
595
|
for (const featureId of featureIds) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
596
|
+
try {
|
|
597
|
+
const context = await this.toolCaller.callTool<FeatureContextPayload>(
|
|
598
|
+
'planner',
|
|
599
|
+
TOOLS.FEATURE_GET_CONTEXT,
|
|
600
|
+
{
|
|
601
|
+
feature_id: featureId,
|
|
602
|
+
},
|
|
603
|
+
);
|
|
603
604
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
605
|
+
const frontMatter = asRecord(asRecord(context.data.state).front_matter);
|
|
606
|
+
const status = readStatus(frontMatter.status);
|
|
607
|
+
if (!isPostQaStatus(status)) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (
|
|
611
|
+
status === STATUS.BLOCKED &&
|
|
612
|
+
resolvePlannerPhaseFromContext(context.data) === 'intake'
|
|
613
|
+
) {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
612
616
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
617
|
+
const existingPlan = asRecord(context.data.plan);
|
|
618
|
+
if (Object.keys(existingPlan).length === 0) {
|
|
619
|
+
await this.appendDecisionLog(featureId, {
|
|
620
|
+
phase: 'post_qa_reconciliation',
|
|
621
|
+
iteration,
|
|
622
|
+
plan_decision: 'unchanged',
|
|
623
|
+
execution_disposition: 'blocked_other',
|
|
624
|
+
reasons: ['missing_plan'],
|
|
625
|
+
status,
|
|
626
|
+
});
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
625
629
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
630
|
+
const planVersion = readPlanVersion(existingPlan);
|
|
631
|
+
if (planVersion == null) {
|
|
632
|
+
await this.appendDecisionLog(featureId, {
|
|
633
|
+
phase: 'post_qa_reconciliation',
|
|
634
|
+
iteration,
|
|
635
|
+
plan_decision: 'unchanged',
|
|
636
|
+
execution_disposition: 'blocked_other',
|
|
637
|
+
reasons: ['existing_plan_version_invalid'],
|
|
638
|
+
status,
|
|
639
|
+
});
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const decision = await this.evaluateReconciliationDecision(
|
|
644
|
+
context.data,
|
|
645
|
+
existingPlan,
|
|
634
646
|
status,
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
647
|
+
);
|
|
648
|
+
if (this.shouldInvokePlannerForPlanTraceContract(decision)) {
|
|
649
|
+
const plannerDecision = await this.workerDecisionRunner.execute({
|
|
650
|
+
role: 'planner',
|
|
651
|
+
featureId,
|
|
652
|
+
contextBundle: {
|
|
653
|
+
...asRecord(context.data),
|
|
654
|
+
plan_trace_contract: structuredClone(decision.planTraceContract),
|
|
655
|
+
},
|
|
656
|
+
instructions:
|
|
657
|
+
'The accepted plan_trace no longer matches the verified manifest contract. Review plan_trace_contract, revise the accepted plan so every verified manifest obligation is correctly traced, remove obsolete obligation mappings, and emit PLAN_SUBMISSION or REQUEST.action=amend_plan. Do not leave the stale plan_trace unchanged.',
|
|
658
|
+
});
|
|
638
659
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
660
|
+
await this.appendDecisionLog(featureId, {
|
|
661
|
+
phase: 'post_qa_reconciliation',
|
|
662
|
+
iteration,
|
|
663
|
+
plan_decision:
|
|
664
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
665
|
+
? 'update_required'
|
|
666
|
+
: 'unchanged',
|
|
667
|
+
execution_disposition:
|
|
668
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
669
|
+
? 'retry_build'
|
|
670
|
+
: decision.executionDisposition,
|
|
671
|
+
status,
|
|
672
|
+
reasons:
|
|
673
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
674
|
+
? normalizeList([
|
|
675
|
+
...decision.reasons,
|
|
676
|
+
'planner_verified_manifest_trace_revision_requested',
|
|
677
|
+
])
|
|
678
|
+
: decision.reasons,
|
|
679
|
+
qa_summary: decision.qaSummary,
|
|
680
|
+
latest_gate_overall: decision.latestGateOverall,
|
|
681
|
+
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
682
|
+
checkpoint_validity: decision.checkpointValidity,
|
|
683
|
+
gate_failure_context: decision.gateFailureContext,
|
|
684
|
+
edge_case_checklist: decision.edgeCaseChecklist,
|
|
685
|
+
plan_trace_contract: decision.planTraceContract,
|
|
686
|
+
});
|
|
655
687
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
670
|
-
? normalizeList([
|
|
671
|
-
...decision.reasons,
|
|
672
|
-
'planner_verified_manifest_trace_revision_requested',
|
|
673
|
-
])
|
|
674
|
-
: decision.reasons,
|
|
675
|
-
qa_summary: decision.qaSummary,
|
|
676
|
-
latest_gate_overall: decision.latestGateOverall,
|
|
677
|
-
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
678
|
-
checkpoint_validity: decision.checkpointValidity,
|
|
679
|
-
gate_failure_context: decision.gateFailureContext,
|
|
680
|
-
edge_case_checklist: decision.edgeCaseChecklist,
|
|
681
|
-
plan_trace_contract: decision.planTraceContract,
|
|
682
|
-
});
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
if (this.shouldInvokePlannerForGateFailure(decision)) {
|
|
691
|
+
const plannerDecision = await this.workerDecisionRunner.execute({
|
|
692
|
+
role: 'planner',
|
|
693
|
+
featureId,
|
|
694
|
+
contextBundle: {
|
|
695
|
+
...asRecord(context.data),
|
|
696
|
+
gate_failure_context: structuredClone(decision.gateFailureContext),
|
|
697
|
+
},
|
|
698
|
+
instructions:
|
|
699
|
+
'A required gate is currently failing with fresh evidence. Review gate_failure_context, revise the accepted plan as needed so the builder can legally investigate and fix the failure, and emit PLAN_SUBMISSION or REQUEST.action=amend_plan when a plan revision is required.',
|
|
700
|
+
});
|
|
683
701
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
702
|
+
await this.appendDecisionLog(featureId, {
|
|
703
|
+
phase: 'post_qa_reconciliation',
|
|
704
|
+
iteration,
|
|
705
|
+
plan_decision:
|
|
706
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
707
|
+
? 'update_required'
|
|
708
|
+
: 'unchanged',
|
|
709
|
+
execution_disposition:
|
|
710
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
711
|
+
? 'retry_build'
|
|
712
|
+
: decision.executionDisposition,
|
|
713
|
+
status,
|
|
714
|
+
reasons:
|
|
715
|
+
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
716
|
+
? normalizeList([...decision.reasons, 'planner_gate_failure_revision_requested'])
|
|
717
|
+
: decision.reasons,
|
|
718
|
+
qa_summary: decision.qaSummary,
|
|
719
|
+
latest_gate_overall: decision.latestGateOverall,
|
|
720
|
+
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
721
|
+
checkpoint_validity: decision.checkpointValidity,
|
|
722
|
+
gate_failure_context: decision.gateFailureContext,
|
|
723
|
+
edge_case_checklist: decision.edgeCaseChecklist,
|
|
724
|
+
plan_trace_contract: decision.planTraceContract,
|
|
725
|
+
});
|
|
697
726
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
703
|
-
? 'update_required'
|
|
704
|
-
: 'unchanged',
|
|
705
|
-
execution_disposition:
|
|
706
|
-
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
707
|
-
? 'retry_build'
|
|
708
|
-
: decision.executionDisposition,
|
|
709
|
-
status,
|
|
710
|
-
reasons:
|
|
711
|
-
plannerDecision.planSubmission || plannerDecision.requestHandled
|
|
712
|
-
? normalizeList([...decision.reasons, 'planner_gate_failure_revision_requested'])
|
|
713
|
-
: decision.reasons,
|
|
714
|
-
qa_summary: decision.qaSummary,
|
|
715
|
-
latest_gate_overall: decision.latestGateOverall,
|
|
716
|
-
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
717
|
-
checkpoint_validity: decision.checkpointValidity,
|
|
718
|
-
gate_failure_context: decision.gateFailureContext,
|
|
719
|
-
edge_case_checklist: decision.edgeCaseChecklist,
|
|
720
|
-
plan_trace_contract: decision.planTraceContract,
|
|
721
|
-
});
|
|
727
|
+
if (plannerDecision.planSubmission || plannerDecision.requestHandled) {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
722
731
|
|
|
723
|
-
if (
|
|
732
|
+
if (decision.planDecision === 'unchanged') {
|
|
733
|
+
await this.appendDecisionLog(featureId, {
|
|
734
|
+
phase: 'post_qa_reconciliation',
|
|
735
|
+
iteration,
|
|
736
|
+
plan_decision: decision.planDecision,
|
|
737
|
+
execution_disposition: decision.executionDisposition,
|
|
738
|
+
status,
|
|
739
|
+
reasons: decision.reasons,
|
|
740
|
+
qa_summary: decision.qaSummary,
|
|
741
|
+
latest_gate_overall: decision.latestGateOverall,
|
|
742
|
+
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
743
|
+
checkpoint_validity: decision.checkpointValidity,
|
|
744
|
+
gate_failure_context: decision.gateFailureContext,
|
|
745
|
+
edge_case_checklist: decision.edgeCaseChecklist,
|
|
746
|
+
plan_trace_contract: decision.planTraceContract,
|
|
747
|
+
});
|
|
724
748
|
continue;
|
|
725
749
|
}
|
|
726
|
-
}
|
|
727
750
|
|
|
728
|
-
|
|
751
|
+
const nextPlan = this.buildUpdatedPlan(existingPlan, planVersion, decision);
|
|
752
|
+
await this.toolCaller.callTool('planner', TOOLS.PLAN_UPDATE, {
|
|
753
|
+
feature_id: featureId,
|
|
754
|
+
expected_plan_version: planVersion,
|
|
755
|
+
plan_json: nextPlan,
|
|
756
|
+
});
|
|
757
|
+
|
|
729
758
|
await this.appendDecisionLog(featureId, {
|
|
730
759
|
phase: 'post_qa_reconciliation',
|
|
731
760
|
iteration,
|
|
732
761
|
plan_decision: decision.planDecision,
|
|
733
762
|
execution_disposition: decision.executionDisposition,
|
|
734
763
|
status,
|
|
764
|
+
expected_plan_version: planVersion,
|
|
765
|
+
next_plan_version: nextPlan.plan_version,
|
|
766
|
+
revision_reason: nextPlan.revision_reason,
|
|
735
767
|
reasons: decision.reasons,
|
|
736
768
|
qa_summary: decision.qaSummary,
|
|
737
769
|
latest_gate_overall: decision.latestGateOverall,
|
|
@@ -741,34 +773,16 @@ export class PlanningWaveExecutor {
|
|
|
741
773
|
edge_case_checklist: decision.edgeCaseChecklist,
|
|
742
774
|
plan_trace_contract: decision.planTraceContract,
|
|
743
775
|
});
|
|
744
|
-
|
|
776
|
+
} catch (error) {
|
|
777
|
+
if (this.isFailRunPolicyError(error)) {
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
781
|
+
console.warn(
|
|
782
|
+
`[post-qa] feature ${featureId} failed, continuing with remaining features: ${msg}`,
|
|
783
|
+
);
|
|
784
|
+
await this.blockFeatureOnUnhandledError(featureId, error);
|
|
745
785
|
}
|
|
746
|
-
|
|
747
|
-
const nextPlan = this.buildUpdatedPlan(existingPlan, planVersion, decision);
|
|
748
|
-
await this.toolCaller.callTool('planner', TOOLS.PLAN_UPDATE, {
|
|
749
|
-
feature_id: featureId,
|
|
750
|
-
expected_plan_version: planVersion,
|
|
751
|
-
plan_json: nextPlan,
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
await this.appendDecisionLog(featureId, {
|
|
755
|
-
phase: 'post_qa_reconciliation',
|
|
756
|
-
iteration,
|
|
757
|
-
plan_decision: decision.planDecision,
|
|
758
|
-
execution_disposition: decision.executionDisposition,
|
|
759
|
-
status,
|
|
760
|
-
expected_plan_version: planVersion,
|
|
761
|
-
next_plan_version: nextPlan.plan_version,
|
|
762
|
-
revision_reason: nextPlan.revision_reason,
|
|
763
|
-
reasons: decision.reasons,
|
|
764
|
-
qa_summary: decision.qaSummary,
|
|
765
|
-
latest_gate_overall: decision.latestGateOverall,
|
|
766
|
-
gate_evidence_freshness: decision.gateEvidenceFreshness,
|
|
767
|
-
checkpoint_validity: decision.checkpointValidity,
|
|
768
|
-
gate_failure_context: decision.gateFailureContext,
|
|
769
|
-
edge_case_checklist: decision.edgeCaseChecklist,
|
|
770
|
-
plan_trace_contract: decision.planTraceContract,
|
|
771
|
-
});
|
|
772
786
|
}
|
|
773
787
|
}
|
|
774
788
|
|
|
@@ -1107,6 +1121,11 @@ export class PlanningWaveExecutor {
|
|
|
1107
1121
|
if (this.providerMode !== 'live') {
|
|
1108
1122
|
throw error;
|
|
1109
1123
|
}
|
|
1124
|
+
// Tool dispatch errors (normalizedResponse) are content errors, not provider failures.
|
|
1125
|
+
// Let them propagate to the outer catch which blocks the feature and continues.
|
|
1126
|
+
if ((error as Record<string, unknown>)?.normalizedResponse) {
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1110
1129
|
const typed = error as { code?: string; message?: string };
|
|
1111
1130
|
const code =
|
|
1112
1131
|
typeof typed.code === 'string' && typed.code.length > 0
|
|
@@ -551,6 +551,9 @@ export class QaWaveExecutor {
|
|
|
551
551
|
if (this.providerMode !== 'live') {
|
|
552
552
|
throw error;
|
|
553
553
|
}
|
|
554
|
+
if ((error as Record<string, unknown>)?.normalizedResponse) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
554
557
|
const typed = error as { code?: string; message?: string };
|
|
555
558
|
const code =
|
|
556
559
|
typeof typed.code === 'string' && typed.code.length > 0
|
|
@@ -2007,6 +2007,46 @@ describe('PlanningWaveExecutor live watchdog and semantic policies', () => {
|
|
|
2007
2007
|
warnSpy.mockRestore();
|
|
2008
2008
|
});
|
|
2009
2009
|
|
|
2010
|
+
it('isolates post-QA reconciliation errors per feature instead of failing the loop', async () => {
|
|
2011
|
+
const contextCallFeatureIds: string[] = [];
|
|
2012
|
+
const toolCaller = {
|
|
2013
|
+
callTool: vi.fn(async (_role: string, toolName: string, args: Record<string, unknown>) => {
|
|
2014
|
+
if (toolName === TOOLS.FEATURE_GET_CONTEXT) {
|
|
2015
|
+
const fid = args.feature_id as string;
|
|
2016
|
+
contextCallFeatureIds.push(fid);
|
|
2017
|
+
if (fid === 'feature-a') {
|
|
2018
|
+
throw { normalizedResponse: { error: { code: 'plan_validation_failed' } } };
|
|
2019
|
+
}
|
|
2020
|
+
// feature-b: not in post-QA status, so it will be skipped via isPostQaStatus
|
|
2021
|
+
return {
|
|
2022
|
+
data: {
|
|
2023
|
+
state: { front_matter: { status: STATUS.PLANNING, version: 1 } },
|
|
2024
|
+
plan: {},
|
|
2025
|
+
intake: {},
|
|
2026
|
+
},
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
return { ok: true, data: {} };
|
|
2030
|
+
}),
|
|
2031
|
+
};
|
|
2032
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
2033
|
+
const executor = new PlanningWaveExecutor({
|
|
2034
|
+
toolCaller: toolCaller as never,
|
|
2035
|
+
planGenerator: { generateInitialPlan: vi.fn() } as never,
|
|
2036
|
+
providerMode: 'live',
|
|
2037
|
+
workerDecisionRunner: { execute: vi.fn() },
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
await executor.runPostQaReconciliation(['feature-a', 'feature-b'], 1);
|
|
2041
|
+
|
|
2042
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
2043
|
+
expect.stringContaining('[post-qa] feature feature-a failed'),
|
|
2044
|
+
);
|
|
2045
|
+
// feature-b was still reached despite feature-a throwing
|
|
2046
|
+
expect(contextCallFeatureIds).toEqual(['feature-a', 'feature-b']);
|
|
2047
|
+
warnSpy.mockRestore();
|
|
2048
|
+
});
|
|
2049
|
+
|
|
2010
2050
|
it('throws provider spawn failure when fail-run policy is configured', async () => {
|
|
2011
2051
|
const toolCaller = createPlanningToolCaller([STATUS.PLANNING]);
|
|
2012
2052
|
const runtimeError = new Error('runtime unavailable') as Error & { code?: string };
|
|
@@ -2,7 +2,12 @@ import { ERROR_CODES } from '../core/error-codes.js';
|
|
|
2
2
|
import { GATE_RESULT, TOOLS } from '../core/constants.js';
|
|
3
3
|
import { buildMergeRepairQaReroute, isBlockedMergeGateFailure } from '../core/merge-repair.js';
|
|
4
4
|
import { callCliTool } from './tooling.js';
|
|
5
|
-
function resolveBlockedRetryStatus(mode) {
|
|
5
|
+
function resolveBlockedRetryStatus(mode, resumePhase) {
|
|
6
|
+
// If recovery knows the phase where the failure occurred, resume there instead of
|
|
7
|
+
// inferring from the gate mode (which may not have been reached).
|
|
8
|
+
if (resumePhase === 'planning' || resumePhase === 'intake') {
|
|
9
|
+
return 'planning';
|
|
10
|
+
}
|
|
6
11
|
if (mode === 'fast') {
|
|
7
12
|
return 'building';
|
|
8
13
|
}
|
|
@@ -75,6 +80,8 @@ export class RetryCommandHandler {
|
|
|
75
80
|
}
|
|
76
81
|
const lastGateMode = statePayload.front_matter.evidence?.last_gate_mode;
|
|
77
82
|
const gates = statePayload.front_matter.gates ?? {};
|
|
83
|
+
const recovery = statePayload.front_matter.recovery;
|
|
84
|
+
const recoveryResumePhase = typeof recovery?.resume_phase === 'string' ? recovery.resume_phase : null;
|
|
78
85
|
const inferredMode = typeof lastGateMode === 'string' && lastGateMode.length > 0
|
|
79
86
|
? lastGateMode
|
|
80
87
|
: status === 'building'
|
|
@@ -163,7 +170,7 @@ export class RetryCommandHandler {
|
|
|
163
170
|
gate_retry_count: 0,
|
|
164
171
|
last_retry_at: null,
|
|
165
172
|
};
|
|
166
|
-
const reroutedStatus = status === 'blocked' ? resolveBlockedRetryStatus(inferredMode) : null;
|
|
173
|
+
const reroutedStatus = status === 'blocked' ? resolveBlockedRetryStatus(inferredMode, recoveryResumePhase) : null;
|
|
167
174
|
if (reroutedStatus) {
|
|
168
175
|
frontMatterPatch.status = reroutedStatus;
|
|
169
176
|
frontMatterPatch.status_reason = `forced_retry:${inferredMode}`;
|
|
@@ -227,7 +234,7 @@ export class RetryCommandHandler {
|
|
|
227
234
|
};
|
|
228
235
|
}
|
|
229
236
|
else if (status === 'blocked') {
|
|
230
|
-
const reroutedStatus = resolveBlockedRetryStatus(inferredMode);
|
|
237
|
+
const reroutedStatus = resolveBlockedRetryStatus(inferredMode, recoveryResumePhase);
|
|
231
238
|
const frontMatterPatch = {
|
|
232
239
|
gate_retry_count: 0,
|
|
233
240
|
last_retry_at: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-command-handler.js","sourceRoot":"","sources":["../../../../apps/control-plane/src/cli/retry-command-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAI/F,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"retry-command-handler.js","sourceRoot":"","sources":["../../../../apps/control-plane/src/cli/retry-command-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -407,6 +407,9 @@ export class BuildWaveExecutor {
|
|
|
407
407
|
if (this.providerMode !== 'live') {
|
|
408
408
|
throw error;
|
|
409
409
|
}
|
|
410
|
+
if (error?.normalizedResponse) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
410
413
|
const typed = error;
|
|
411
414
|
const code = typeof typed.code === 'string' && typed.code.length > 0
|
|
412
415
|
? typed.code
|