fix(server,kernel): freestyle survives kill/restart at every phase (B4)

Three gaps found by live QA (kill between planning-complete and plan lock):
- launchSessionResumeWithRehydrate never handed off to FreestyleDriver, so a
  resumed freestyle session stranded at planning-complete with no plan lock
  and no phase 2; now locks+runs when planning completed and no
  ExecutionPlanLockedEvent exists yet.
- resume route 400'd on phase-2 sessions (workflowId freestyle-<sid> is
  compiled, never registered); now recompiles the locked plan after
  rehydrating the artifact cache.
- orchestrator.resume threw when currentStageId was terminal ('done') or
  unknown to the graph; now returns WorkflowResult.Completed.
This commit is contained in:
2026-06-11 22:35:51 +04:00
parent 99bca1703b
commit e503a28db2
4 changed files with 48 additions and 1 deletions
@@ -219,6 +219,15 @@ class DefaultSessionOrchestrator(
): WorkflowResult {
val stageId = orchestrationRepository.getState(sessionId).currentStageId
?: return WorkflowResult.Failed(sessionId, "resume: no currentStageId", retryExhausted = false)
if (!graph.stages.containsKey(stageId)) {
// Terminal pseudo-stage ("done") or a stage from another graph — nothing to
// re-enter. Treat as already completed instead of throwing inside enterStage.
log.info(
"[Orchestrator] resume: session={} stage={} not in graph '{}' — already terminal",
sessionId.value, stageId.value, graph.id,
)
return WorkflowResult.Completed(sessionId, stageId)
}
log.info("[Orchestrator] resuming session={} workflow={} stage={}", sessionId.value, graph.id, stageId.value)
val base = ExecutionContext(graph, sessionId, 0, stageId, config, null, null)
val enriched = base.enrich()