返回 CodeWhale
goal_loop.rs
根目录 / crates / runtime / src / goal_loop.rs
1 //! Goal loop orchestrator — the persistent-objective control layer (#3215, and
2 //! its lineage #891 / #1976 / #2058 / #2029).
3 //!
4 //! This is the **Workflow goal layer**: the decision core that turns a one-shot
5 //! `/goal` into a persistent work loop. Given the durable goal status, the
6 //! accumulated usage (from the per-goal accounting wired in `crates/state`
7 //! `record_thread_goal_usage`), and a budget, it decides whether to **continue**
8 //! (re-dispatch another worker turn toward the objective) or **stop** with a
9 //! terminal status. It is the orchestrator in the Workflow≈ultracode mapping —
10 //! the loop that fans work out to workers (`worker_profile`) and verifies before
11 //! committing.
12 //!
13 //! Scope: **decision logic + types**. The engine (`core/engine.rs`) reads the
14 //! `SharedGoalState` snapshot after each turn and calls `decide_continuation`
15 //! to decide whether to re-dispatch. For operate-mode goals the terminal stops
16 //! are a verified completion, a blocked report, the stall pause that fires
17 //! when a critical verifier reports the same gap set
18 //! [`MAX_REPEATED_GAP_PASSES`] times running (enforced in
19 //! `GoalState::record_not_achieved`), and the opt-in continuation backstop
20 //! (`[goal] max_continuations`); token/time accounting stays visible as
21 //! telemetry but does not gate continuation — spend is bounded by stalling,
22 //! not by a pass count, like grokbuild (`DEFAULT_AGENT_BUDGET` as call cap)
23 //! and kimicode swarm (`turnBudget` per-task, resumable after
24 //! budget-reached). Log when the backstop fires.
25
26 use std::time::Duration;
27
28 /// Default automatic cross-turn continuation policy for one goal run (#5052).
29 ///
30 /// Goals are unlimited by default: completion, blocked status, or explicit
31 /// user control ends the run. Operators who want a circuit breaker can opt in
32 /// with `[goal] max_continuations`; `0` keeps the default unlimited behavior.
33 pub const DEFAULT_MAX_GOAL_CONTINUATIONS: u32 = 0;
34
35 /// Default per-engine-turn step allowance while a goal is active (#5994).
36 /// Deliberate maintainer policy, not a measured number: five times the
37 /// ordinary interactive allowance (200), so intentional goal work has room
38 /// while every provider turn stays finite. The count of continuation passes
39 /// remains governed separately (`[goal] max_continuations`).
40 pub const DEFAULT_GOAL_MAX_STEPS: u32 = 1_000;
41
42 /// How many consecutive critical `not_achieved` reviews naming the *same*
43 /// normalized gap set a goal may accumulate before it pauses itself with
44 /// `GoalPauseReason::NoProgress`.
45 ///
46 /// This is the bound the continuation prompt promises the model, and it is the
47 /// only default stop on a goal run: `DEFAULT_MAX_GOAL_CONTINUATIONS` is `0`, so
48 /// without it the loop runs until the model volunteers a terminal status.
49 /// Enforcement lives in `GoalState::record_not_achieved`, and it reuses the
50 /// existing pause authority rather than adding a second one — both continuation
51 /// dispatchers already refuse to re-dispatch a non-active goal, and
52 /// `RuntimeThreadManager` already mirrors a non-limit pause into the durable
53 /// `ThreadGoalStatus::Paused`, so the stop survives a restart and needs an
54 /// explicit resume.
55 ///
56 /// The counter is `1` on the first report of a gap set, so `3` means the
57 /// verifier named identical remaining work three times running: two whole
58 /// continuation passes that moved nothing the verifier can see. Two is too
59 /// eager — one pass legitimately fails to land a fix and retries — and anything
60 /// larger just buys more identical passes.
61 pub const MAX_REPEATED_GAP_PASSES: u32 = 3;
62
63 /// Upper bound for one between-turn quiet period. A day is long enough for
64 /// coordinator cadences while preventing an accidental giant integer from
65 /// becoming a practically uninterruptible-looking schedule receipt.
66 pub const MAX_GOAL_CONTINUATION_DELAY_SECONDS: u64 = 24 * 60 * 60;
67
68 /// Terminal or active state of a persistent goal.
69 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
70 pub enum GoalRunStatus {
71 /// Still working toward the objective.
72 Active,
73 /// The objective was achieved (the model self-reported done and, ideally, a
74 /// verifier confirmed — see `GoalGate`).
75 Completed,
76 /// The model reported it is blocked and needs the user.
77 Blocked,
78 }
79
80 /// Why the loop stopped, for a terminal decision.
81 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
82 pub enum StopReason {
83 /// Objective achieved.
84 Completed,
85 /// Model reported blocked.
86 Blocked,
87 /// Continuation circuit-breaker tripped (too many continuations without a
88 /// terminal signal).
89 ContinuationLimit,
90 /// The goal's token budget was reached while `[goal] enforce_token_budget`
91 /// opted the budget into a hard stop (#6013). Default-off: without the
92 /// opt-in the budget stays advisory telemetry and never produces this.
93 BudgetLimit,
94 }
95
96 /// Accumulated, durable progress for a goal run. Mirrors the fields wired by
97 /// `crates/state` `record_thread_goal_usage` (tokens_used / time_used_seconds)
98 /// plus a continuation counter the loop maintains.
99 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
100 pub struct GoalProgress {
101 pub tokens_used: u64,
102 pub time_used_seconds: u64,
103 pub continuations: u32,
104 }
105
106 /// The optional token/time bounds on a goal run. `None` fields mean unbounded
107 /// for that resource; the continuation backstop (`max_continuations`) still
108 /// applies unless configured to `0`.
109 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
110 pub struct GoalBudget {
111 pub token_budget: Option<u64>,
112 pub time_budget_seconds: Option<u64>,
113 /// Whether `token_budget` stops the run when reached (#6013). `false`
114 /// keeps the default advisory behavior: crossing the budget logs and
115 /// continues. There is no time-budget enforcement because goals carry no
116 /// `time_budget` field — only `token_budget` can gate.
117 pub enforce_token_budget: bool,
118 /// Safety backstop on automatic continuation passes (#5052). `0` disables
119 /// the backstop: only terminal status stops the run.
120 pub max_continuations: u32,
121 }
122
123 impl GoalBudget {
124 /// No token or time cap. Terminal status, user control, and the default
125 /// continuation backstop still stop the run.
126 pub const fn unbounded() -> Self {
127 Self {
128 token_budget: None,
129 time_budget_seconds: None,
130 enforce_token_budget: false,
131 max_continuations: DEFAULT_MAX_GOAL_CONTINUATIONS,
132 }
133 }
134
135 /// A token budget for telemetry/UI. It never pauses an unbounded goal.
136 pub const fn with_token_budget(token_budget: u64) -> Self {
137 Self {
138 token_budget: Some(token_budget),
139 time_budget_seconds: None,
140 enforce_token_budget: false,
141 max_continuations: DEFAULT_MAX_GOAL_CONTINUATIONS,
142 }
143 }
144
145 /// Opt the token budget into a hard stop (`[goal] enforce_token_budget`).
146 /// With no `token_budget` set this is a no-op by construction — there is
147 /// no ceiling to reach.
148 #[must_use]
149 pub const fn with_enforced_token_budget(mut self, enforce: bool) -> Self {
150 self.enforce_token_budget = enforce;
151 self
152 }
153
154 /// Override the continuation backstop (`0` = unlimited until terminal
155 /// status).
156 #[must_use]
157 pub const fn with_max_continuations(mut self, max_continuations: u32) -> Self {
158 self.max_continuations = max_continuations;
159 self
160 }
161 }
162
163 /// The decision the loop makes after each worker turn.
164 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
165 pub enum ContinuationDecision {
166 /// Re-dispatch another turn toward the objective.
167 Continue,
168 /// Stop; the goal run is terminal.
169 Stop(StopReason),
170 }
171
172 /// Decide whether a persistent goal run should continue after a turn.
173 ///
174 /// Precedence (most authoritative first):
175 /// 1. A terminal model status (Completed / Blocked) ends the run.
176 /// 2. The configurable continuation backstop stops a pathological loop
177 /// (skipped entirely when configured to `0`).
178 /// 3. Otherwise continue — the loop runs to the completion gate, not to a
179 /// fixed pass count (#5052). Token/time budgets are advisory telemetry;
180 /// they are surfaced in the UI but do not stop the run (unbounded).
181 #[must_use]
182 pub fn decide_continuation(
183 status: GoalRunStatus,
184 progress: GoalProgress,
185 budget: GoalBudget,
186 ) -> ContinuationDecision {
187 // 1. Terminal model signal wins.
188 match status {
189 GoalRunStatus::Completed => return ContinuationDecision::Stop(StopReason::Completed),
190 GoalRunStatus::Blocked => return ContinuationDecision::Stop(StopReason::Blocked),
191 GoalRunStatus::Active => {}
192 }
193
194 // 2. Token budget: advisory telemetry by default, or a hard stop when
195 // `[goal] enforce_token_budget` opts in (#6013). Time stays advisory —
196 // goals carry no time budget to enforce against.
197 if budget
198 .token_budget
199 .is_some_and(|limit| progress.tokens_used >= limit)
200 {
201 if budget.enforce_token_budget {
202 tracing::info!(
203 tokens_used = progress.tokens_used,
204 token_budget = ?budget.token_budget,
205 "goal token budget reached; stopping ([goal] enforce_token_budget)"
206 );
207 return ContinuationDecision::Stop(StopReason::BudgetLimit);
208 }
209 tracing::debug!(
210 tokens_used = progress.tokens_used,
211 token_budget = ?budget.token_budget,
212 "goal over token budget but continuing (unbounded)"
213 );
214 }
215 if let Some(secs) = budget.time_budget_seconds
216 && progress.time_used_seconds >= secs
217 {
218 tracing::debug!(
219 time_used_seconds = progress.time_used_seconds,
220 time_budget_seconds = secs,
221 "goal over time budget but continuing (unbounded)"
222 );
223 }
224
225 // 3. Runaway-cost backstop. This deliberately uses the already-durable
226 // continuation counter instead of adding verifier fingerprints or another
227 // orchestration subsystem. `0` disables it — budget/terminal stops only.
228 if budget.max_continuations > 0 && progress.continuations >= budget.max_continuations {
229 tracing::warn!(
230 continuations = progress.continuations,
231 max_continuations = budget.max_continuations,
232 "goal continuation backstop fired: no terminal signal after the configured \
233 continuation limit ([goal] max_continuations)"
234 );
235 return ContinuationDecision::Stop(StopReason::ContinuationLimit);
236 }
237
238 // 4. Keep going.
239 ContinuationDecision::Continue
240 }
241
242 /// Outcome of waiting out the between-continuation quiet period.
243 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
244 pub enum ContinuationWaitOutcome {
245 /// The quiet period elapsed — dispatch the continuation.
246 Elapsed,
247 /// Cancelled during the quiet period — never dispatch.
248 Cancelled,
249 }
250
251 /// Compute the quiet-period wait for a configured between-continuation delay.
252 /// `None` continues immediately (unset or zero delay); a positive delay
253 /// returns the capped wait shared by every dispatch path so no caller can
254 /// construct an effectively uninterruptible schedule receipt.
255 #[must_use]
256 pub const fn continuation_wait(delay_seconds: u64) -> Option<Duration> {
257 if delay_seconds == 0 {
258 None
259 } else {
260 Some(Duration::from_secs(
261 if delay_seconds > MAX_GOAL_CONTINUATION_DELAY_SECONDS {
262 MAX_GOAL_CONTINUATION_DELAY_SECONDS
263 } else {
264 delay_seconds
265 },
266 ))
267 }
268 }
269
270 /// Wait out the between-continuation quiet period, honoring cancellation.
271 /// `None` resolves immediately so callers have a single dispatch gate: to
272 /// `Cancelled` when the token is already cancelled, otherwise `Elapsed`.
273 /// Cancellation is biased and always wins over a racing expiry — the same
274 /// semantics as the interactive cadence (#5508), including a zero delay. Two dispatchers await
275 /// this gate: the turn loop's intra-turn passes (every session), and the
276 /// runtime host's cross-turn re-arm for host-managed engines
277 /// (`RuntimeThreadManager::spawn_goal_continuation`), which never
278 /// self-continue.
279 pub async fn await_continuation_wait(
280 wait: Option<Duration>,
281 cancel_token: &tokio_util::sync::CancellationToken,
282 ) -> ContinuationWaitOutcome {
283 if cancel_token.is_cancelled() {
284 return ContinuationWaitOutcome::Cancelled;
285 }
286 let Some(wait) = wait else {
287 return ContinuationWaitOutcome::Elapsed;
288 };
289 tokio::select! {
290 biased;
291 () = cancel_token.cancelled() => ContinuationWaitOutcome::Cancelled,
292 () = tokio::time::sleep(wait) => ContinuationWaitOutcome::Elapsed,
293 }
294 }
295
296 /// Whether the durable token usage has reached an *enforced* token budget —
297 /// the same condition on which [`decide_continuation`] stops with
298 /// [`StopReason::BudgetLimit`]. Budgets are telemetry-only unless
299 /// `[goal] enforce_token_budget` opts in, so without it this stays false and
300 /// crossing a budget never closes the outbound gate. Preview and the live
301 /// continuation loop share this predicate so they cannot disagree about spend.
302 #[must_use]
303 pub const fn token_budget_exhausted(progress: GoalProgress, budget: GoalBudget) -> bool {
304 budget.enforce_token_budget
305 && match budget.token_budget {
306 Some(limit) => progress.tokens_used >= limit,
307 None => false,
308 }
309 }
310
311 /// Whether a stop reason represents success (Completed) vs. an early/forced exit.
312 /// Useful for the UI/status projection (#2666 token/time visibility).
313 #[must_use]
314 pub fn is_success(reason: StopReason) -> bool {
315 matches!(reason, StopReason::Completed)
316 }
317
318 #[cfg(test)]
319 mod tests {
320 use super::*;
321
322 #[test]
323 fn completed_status_stops_with_success() {
324 let d = decide_continuation(
325 GoalRunStatus::Completed,
326 GoalProgress::default(),
327 GoalBudget::unbounded(),
328 );
329 assert_eq!(d, ContinuationDecision::Stop(StopReason::Completed));
330 assert!(is_success(StopReason::Completed));
331 }
332
333 #[test]
334 fn blocked_status_stops_without_success() {
335 let d = decide_continuation(
336 GoalRunStatus::Blocked,
337 GoalProgress::default(),
338 GoalBudget::unbounded(),
339 );
340 assert_eq!(d, ContinuationDecision::Stop(StopReason::Blocked));
341 assert!(!is_success(StopReason::Blocked));
342 }
343
344 #[test]
345 fn active_under_budget_continues() {
346 let progress = GoalProgress {
347 tokens_used: 10,
348 time_used_seconds: 5,
349 continuations: 2,
350 };
351 let budget = GoalBudget {
352 token_budget: Some(1000),
353 time_budget_seconds: Some(600),
354 enforce_token_budget: false,
355 max_continuations: DEFAULT_MAX_GOAL_CONTINUATIONS,
356 };
357 assert_eq!(
358 decide_continuation(GoalRunStatus::Active, progress, budget),
359 ContinuationDecision::Continue
360 );
361 }
362
363 #[test]
364 fn default_goal_has_no_continuation_limit() {
365 let progress = GoalProgress {
366 continuations: 10_000,
367 ..GoalProgress::default()
368 };
369 assert_eq!(
370 decide_continuation(GoalRunStatus::Active, progress, GoalBudget::unbounded()),
371 ContinuationDecision::Continue
372 );
373 }
374
375 #[test]
376 fn explicit_continuation_limit_stops_run() {
377 let configured_limit = 100;
378 let progress = GoalProgress {
379 continuations: configured_limit,
380 ..GoalProgress::default()
381 };
382 let budget = GoalBudget::unbounded().with_max_continuations(configured_limit);
383 assert_eq!(
384 decide_continuation(GoalRunStatus::Active, progress, budget),
385 ContinuationDecision::Stop(StopReason::ContinuationLimit)
386 );
387 }
388
389 #[test]
390 fn operate_goal_continues_past_ten_when_budget_remains() {
391 // #5052 regression: the old hardcoded cap of 10 must not be a terminal
392 // stop. With no terminal signal, pass 10, 11, and far beyond keep
393 // continuing because the default has no hidden ceiling.
394 for continuations in [10, 11, 100, 10_000] {
395 let progress = GoalProgress {
396 tokens_used: 5_000,
397 time_used_seconds: 300,
398 continuations,
399 };
400 let budget = GoalBudget::with_token_budget(1_000_000);
401 assert_eq!(
402 decide_continuation(GoalRunStatus::Active, progress, budget),
403 ContinuationDecision::Continue,
404 "pass {continuations} must continue toward the completion gate",
405 );
406 }
407 }
408
409 #[test]
410 fn configured_backstop_halts_pathological_loop() {
411 let backstop = 25;
412 let progress = GoalProgress {
413 continuations: backstop,
414 ..GoalProgress::default()
415 };
416 let budget = GoalBudget::unbounded().with_max_continuations(backstop);
417 assert_eq!(
418 decide_continuation(GoalRunStatus::Active, progress, budget),
419 ContinuationDecision::Stop(StopReason::ContinuationLimit)
420 );
421 }
422
423 #[test]
424 fn zero_backstop_is_unlimited_and_budget_advisory() {
425 // 0 = unlimited-with-budget-stops: no continuation count ends the run…
426 let progress = GoalProgress {
427 continuations: 10_000,
428 ..GoalProgress::default()
429 };
430 let budget = GoalBudget::unbounded().with_max_continuations(0);
431 assert_eq!(
432 decide_continuation(GoalRunStatus::Active, progress, budget),
433 ContinuationDecision::Continue
434 );
435
436 // exceeded token budget is advisory — must still continue (unbounded)
437 let progress = GoalProgress {
438 tokens_used: 1_000,
439 continuations: 10_000,
440 ..GoalProgress::default()
441 };
442 let budget = GoalBudget::with_token_budget(1_000).with_max_continuations(0);
443 assert_eq!(
444 decide_continuation(GoalRunStatus::Active, progress, budget),
445 ContinuationDecision::Continue,
446 "budget advisory — must continue even when over budget"
447 );
448 }
449
450 #[test]
451 fn enforced_token_budget_stops_the_run() {
452 let progress = GoalProgress {
453 tokens_used: 1000,
454 continuations: 1,
455 ..GoalProgress::default()
456 };
457 let budget = GoalBudget::with_token_budget(1000).with_enforced_token_budget(true);
458 assert_eq!(
459 decide_continuation(GoalRunStatus::Active, progress, budget),
460 ContinuationDecision::Stop(StopReason::BudgetLimit),
461 "enforce_token_budget makes an exhausted token budget terminal"
462 );
463 assert!(!is_success(StopReason::BudgetLimit));
464 }
465
466 #[test]
467 fn enforce_flag_without_a_token_budget_cannot_stop() {
468 // A goal created without `token_budget` has no ceiling to reach — the
469 // flag must not invent one.
470 let progress = GoalProgress {
471 tokens_used: u64::MAX / 2,
472 continuations: 1,
473 ..GoalProgress::default()
474 };
475 let budget = GoalBudget::unbounded().with_enforced_token_budget(true);
476 assert_eq!(
477 decide_continuation(GoalRunStatus::Active, progress, budget),
478 ContinuationDecision::Continue
479 );
480 }
481
482 #[test]
483 fn enforced_budget_still_yields_to_a_terminal_status() {
484 let budget = GoalBudget::with_token_budget(1000).with_enforced_token_budget(true);
485 assert_eq!(
486 decide_continuation(GoalRunStatus::Completed, GoalProgress::default(), budget),
487 ContinuationDecision::Stop(StopReason::Completed),
488 "a clean completion outranks the budget gate"
489 );
490 }
491
492 #[test]
493 fn token_budget_is_advisory_not_terminal() {
494 let progress = GoalProgress {
495 tokens_used: 1000,
496 continuations: 1,
497 ..GoalProgress::default()
498 };
499 let budget = GoalBudget::with_token_budget(1000);
500 assert_eq!(
501 decide_continuation(GoalRunStatus::Active, progress, budget),
502 ContinuationDecision::Continue,
503 "token budget is advisory — unbounded run must continue"
504 );
505 }
506
507 #[test]
508 fn time_budget_is_advisory_not_terminal() {
509 let progress = GoalProgress {
510 time_used_seconds: 601,
511 continuations: 1,
512 ..GoalProgress::default()
513 };
514 let budget = GoalBudget {
515 token_budget: None,
516 time_budget_seconds: Some(600),
517 enforce_token_budget: false,
518 max_continuations: DEFAULT_MAX_GOAL_CONTINUATIONS,
519 };
520 assert_eq!(
521 decide_continuation(GoalRunStatus::Active, progress, budget),
522 ContinuationDecision::Continue,
523 "time budget is advisory — unbounded run must continue"
524 );
525 }
526
527 #[test]
528 fn terminal_status_outranks_remaining_budget() {
529 // Completed wins even if there is plenty of budget left.
530 let progress = GoalProgress::default();
531 let budget = GoalBudget {
532 token_budget: Some(1_000_000),
533 time_budget_seconds: Some(86_400),
534 enforce_token_budget: false,
535 max_continuations: DEFAULT_MAX_GOAL_CONTINUATIONS,
536 };
537 assert_eq!(
538 decide_continuation(GoalRunStatus::Completed, progress, budget),
539 ContinuationDecision::Stop(StopReason::Completed)
540 );
541 }
542
543 #[test]
544 fn continuation_wait_honors_configured_delay() {
545 assert_eq!(
546 continuation_wait(300),
547 Some(Duration::from_secs(300)),
548 "a positive configured delay must become the quiet-period wait"
549 );
550 assert_eq!(
551 continuation_wait(MAX_GOAL_CONTINUATION_DELAY_SECONDS + 1),
552 Some(Duration::from_secs(MAX_GOAL_CONTINUATION_DELAY_SECONDS)),
553 "the shared cap must bound an oversized configured delay"
554 );
555 }
556
557 #[test]
558 fn zero_delay_continues_immediately() {
559 assert_eq!(
560 continuation_wait(0),
561 None,
562 "an unset or zero delay must dispatch immediately"
563 );
564 }
565
566 #[tokio::test]
567 async fn cancellation_wins_over_pending_quiet_period() {
568 let cancel_token = tokio_util::sync::CancellationToken::new();
569 let canceller = cancel_token.clone();
570 tokio::spawn(async move {
571 tokio::time::sleep(Duration::from_millis(10)).await;
572 canceller.cancel();
573 });
574 assert_eq!(
575 await_continuation_wait(
576 continuation_wait(MAX_GOAL_CONTINUATION_DELAY_SECONDS),
577 &cancel_token,
578 )
579 .await,
580 ContinuationWaitOutcome::Cancelled,
581 "an explicit cancel during the quiet period must win and never dispatch"
582 );
583 }
584
585 #[test]
586 fn exhausted_budget_predicate_matches_the_live_stop() {
587 let progress = GoalProgress {
588 tokens_used: 1_000,
589 ..GoalProgress::default()
590 };
591 for enforce in [false, true] {
592 let budget = GoalBudget {
593 token_budget: Some(1_000),
594 time_budget_seconds: None,
595 enforce_token_budget: enforce,
596 max_continuations: 0,
597 };
598 assert_eq!(
599 token_budget_exhausted(progress, budget),
600 decide_continuation(GoalRunStatus::Active, progress, budget)
601 == ContinuationDecision::Stop(StopReason::BudgetLimit),
602 "preview must report exactly the budget stop the live loop takes (enforce={enforce})"
603 );
604 }
605 }
606
607 #[tokio::test]
608 async fn cancellation_wins_over_a_zero_delay() {
609 let cancel_token = tokio_util::sync::CancellationToken::new();
610 cancel_token.cancel();
611 assert_eq!(
612 await_continuation_wait(continuation_wait(0), &cancel_token).await,
613 ContinuationWaitOutcome::Cancelled,
614 "a cancelled goal with no quiet period must never dispatch"
615 );
616 }
617
618 #[tokio::test]
619 async fn elapsed_quiet_period_dispatches() {
620 let cancel_token = tokio_util::sync::CancellationToken::new();
621 assert_eq!(
622 await_continuation_wait(None, &cancel_token).await,
623 ContinuationWaitOutcome::Elapsed,
624 "an unset wait must gate the dispatch through immediately"
625 );
626 assert_eq!(
627 await_continuation_wait(Some(Duration::from_millis(1)), &cancel_token).await,
628 ContinuationWaitOutcome::Elapsed,
629 "an expired quiet period must dispatch"
630 );
631 }
632 }
633
633 lines RUST