Skip to main content

awaken_runtime/agent/state/
pending_work.rs

1//! PendingWork state key — plugins set this to prevent NaturalEnd.
2//!
3//! When any plugin has work outstanding (e.g. background tasks still running),
4//! it sets `PendingWork` to `true`. The orchestrator checks this at NaturalEnd
5//! and enters `Waiting("awaiting_tasks")` instead of terminating.
6
7use crate::state::StateKey;
8use serde::{Deserialize, Serialize};
9
10/// Whether the run has outstanding work that should prevent NaturalEnd.
11///
12/// Plugins write `true` when they have pending work (e.g. background tasks).
13/// The orchestrator reads this at NaturalEnd to decide whether to terminate
14/// or enter a waiting state.
15#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
16pub struct PendingWorkState {
17    pub has_pending: bool,
18}
19
20pub struct PendingWorkKey;
21
22impl StateKey for PendingWorkKey {
23    const KEY: &'static str = "__runtime.pending_work";
24    type Value = PendingWorkState;
25    type Update = bool;
26
27    fn apply(value: &mut Self::Value, update: Self::Update) {
28        value.has_pending = update;
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn default_is_no_pending() {
38        let state = PendingWorkState::default();
39        assert!(!state.has_pending);
40    }
41
42    #[test]
43    fn update_sets_pending() {
44        let mut state = PendingWorkState::default();
45        PendingWorkKey::apply(&mut state, true);
46        assert!(state.has_pending);
47    }
48
49    #[test]
50    fn update_clears_pending() {
51        let mut state = PendingWorkState { has_pending: true };
52        PendingWorkKey::apply(&mut state, false);
53        assert!(!state.has_pending);
54    }
55}