cancel method
Cancels a mission by id and returns the salvaged result (FR-003, FR-004, FR-005, FR-006).
Cancelling the leading mission completes the whole coalescing group with
a cancelled_partial outcome; all subscribers (original + coalesced)
receive that salvaged outcome. Per-subscriber policies (continue /
escalate / serve-partials) are not yet wired up — see issue #388.
Implementation
Future<MissionOutcome> cancel(String missionId) async {
final canonical = _missionIdToKey[missionId];
if (canonical == null) {
throw StateError('Mission not found: $missionId');
}
final group = _groups[canonical];
if (group == null) {
throw StateError('Mission not active: $missionId');
}
final token =
group.cancelToken ??
CancelToken(gracePeriod: _config.cancellationGracePeriod);
// Salvage + complete the group synchronously BEFORE triggering
// disposal. This ensures [submit] sees the salvaged outcome when
// it resumes after the executor returns (the executor unblocks the
// moment the disposal race starts, before this method runs its
// post-disposal salvage).
if (!group.isCompleted) {
final salvagedOutcome = _salvager.salvage(group.mission);
group.complete(salvagedOutcome);
}
// Trigger the grace-period disposal race. The executor's
// `await cancelToken.onSettled` resolves when this completes.
await runCancellation(token, group.handles);
_cache.store(group.mission.key, group.mission.outcome!);
return group.mission.outcome!;
}