fix(runtime): cancel de background task immédiat et fiable depuis le workstate (#199, QA verte)
Transforme l'annulation d'une background task en contrat immédiat, observable et non-best-effort: - domain/background_task: états Cancelling/Cancelled/CancelFailed et transitions idempotentes; un kill PTY qui échoue est surfacé en CancelFailed plutôt qu'en succès silencieux. - infrastructure/runner + store: cancel effectif de la task ciblée, reconcile marque Cancelling même sans live handle, et départage CancelFailed des tâches pending au boot. - application/background + workstate + orchestrator/wake: chemin de cancel initié depuis le workstate, projection synchronisée de l'état (pas d'attente d'un cycle ambigu). - frontend: projection workstate de l'état cancelled/cancelling/cancelFailed (cancellingRow/cancelFailedRow/cancelButtons), normalization et services. Tests QA verts: background_task_cancel, background_task_runner cancel_wins_even_if_output_is_still_available, background_task_store reconcile_marks_cancelling_without_live_handle_cancel_failed_and_pending, workstate.test.tsx.
This commit is contained in:
@ -139,11 +139,14 @@ impl CommandBackgroundRunner {
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
// Reap the real exit status (works for a natural exit and a forced kill).
|
||||
let exit_code = pty
|
||||
.kill(&control.pty_handle)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|status| status.code);
|
||||
let exit_code = match outcome {
|
||||
WaitOutcome::Cancelled => None,
|
||||
WaitOutcome::Expired | WaitOutcome::Exited => pty
|
||||
.kill(&control.pty_handle)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|status| status.code),
|
||||
};
|
||||
|
||||
let finished_at_ms = u64::try_from(clock.now_millis().max(0)).unwrap_or(0);
|
||||
let result = match outcome {
|
||||
@ -281,8 +284,12 @@ impl BackgroundTaskRunner for CommandBackgroundRunner {
|
||||
.cloned();
|
||||
// Unknown task ⇒ already terminal or never ours: cancellation is idempotent.
|
||||
if let Some(control) = control {
|
||||
self.pty
|
||||
.kill(&control.pty_handle)
|
||||
.await
|
||||
.map_err(|e| BackgroundTaskPortError::Runner(format!("pty kill failed: {e}")))?;
|
||||
control.cancelled.store(true, Ordering::SeqCst);
|
||||
control.cancel.notify_one();
|
||||
control.cancel.notify_waiters();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user