fix(server): boot/event-driven resume paths get rehydrate + freestyle handoff
resumeAbandonedSessions (boot pickup of RUNNING sessions) and the restart-while-approval-pending subscription still used the bare launchSessionResume: no artifact-cache rehydrate and no freestyle plan-lock handoff. A freestyle session killed mid-planning was grabbed by the boot path before the operator could POST /resume, completed the planning graph, and stranded — WorkflowCompleted but never locked (live repro: session b36b0e45, 2026-06-12). Both paths now use launchSessionResumeWithRehydrate; the bare launcher is deleted.
This commit is contained in:
@@ -168,7 +168,7 @@ class ServerModule(
|
||||
val orchState = orchestrationRepository.getState(sessionId)
|
||||
if (orchState.status != OrchestrationStatus.RUNNING) return@onEach
|
||||
val graph = workflowRegistry.find(orchState.workflowId) ?: return@onEach
|
||||
launchSessionResume(sessionId, graph)
|
||||
launchSessionResumeWithRehydrate(sessionId, graph)
|
||||
}
|
||||
.launchIn(moduleScope)
|
||||
|
||||
@@ -309,23 +309,12 @@ class ServerModule(
|
||||
)
|
||||
}
|
||||
|
||||
private fun launchSessionResume(sessionId: SessionId, graph: com.correx.core.transitions.graph.WorkflowGraph) {
|
||||
activeSessionJobs.computeIfAbsent(sessionId) {
|
||||
moduleScope.launch {
|
||||
runCatching {
|
||||
orchestrator.resume(sessionId, graph, orchestrationConfig())
|
||||
}.onFailure { e ->
|
||||
log.error("resumeSession: session={} failed", sessionId.value, e)
|
||||
}
|
||||
activeSessionJobs.remove(sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rehydrates [artifactContentCache] from durable events and then resumes the session.
|
||||
* Called by [POST /sessions/{id}/resume] to restart a session after a server restart
|
||||
* without re-running already-completed stages.
|
||||
* Used by [POST /sessions/{id}/resume], boot-time abandoned-session pickup, and the
|
||||
* restart-while-approval-pending subscription — every resume needs the rehydrate
|
||||
* (pre-restart `needs`-artifacts live only in events/CAS) and the freestyle handoff
|
||||
* (a planning graph that completes on resume must still lock and run phase 2).
|
||||
*/
|
||||
fun launchSessionResumeWithRehydrate(sessionId: SessionId, graph: com.correx.core.transitions.graph.WorkflowGraph) {
|
||||
activeSessionJobs.computeIfAbsent(sessionId) {
|
||||
@@ -365,7 +354,7 @@ class ServerModule(
|
||||
return@forEach
|
||||
}
|
||||
log.info("resumeAbandoned: launching session={} workflow={}", sessionId.value, orchState.workflowId)
|
||||
launchSessionResume(sessionId, graph)
|
||||
launchSessionResumeWithRehydrate(sessionId, graph)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user