khora_core/agent/timing.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Execution timing declarations for agents.
16
17use std::time::Duration;
18
19use super::dependency::AgentDependency;
20use super::execution_phase::ExecutionPhase;
21
22/// Declares when and how an agent should execute within the frame pipeline.
23///
24/// Each agent provides its timing via `Agent::execution_timing()`. The
25/// Scheduler uses this information to order and potentially skip agents
26/// based on the current phase, budget, and dependencies.
27///
28/// **Note**: Mode filtering is done at registration time via
29/// `DccService::register_agent_for_mode()`, not via this struct.
30#[derive(Debug, Clone)]
31pub struct ExecutionTiming {
32 /// Phases where this agent CAN execute.
33 pub allowed_phases: Vec<ExecutionPhase>,
34 /// Default phase if GORNA doesn't specify one.
35 pub default_phase: ExecutionPhase,
36 /// Priority within the phase (higher = executed earlier).
37 pub priority: f32,
38 /// Importance for budget management.
39 pub importance: AgentImportance,
40 /// Fixed timestep. If None, executes every frame.
41 pub fixed_timestep: Option<Duration>,
42 /// Dependencies on other agents.
43 pub dependencies: Vec<AgentDependency>,
44}
45
46impl Default for ExecutionTiming {
47 fn default() -> Self {
48 Self {
49 allowed_phases: ExecutionPhase::DEFAULT_ORDER.to_vec(),
50 default_phase: ExecutionPhase::OUTPUT,
51 priority: 0.5,
52 importance: AgentImportance::Important,
53 fixed_timestep: None,
54 dependencies: Vec::new(),
55 }
56 }
57}
58
59/// How critical an agent is for frame correctness — and therefore whether GORNA
60/// budget arbitration may skip it under pressure.
61///
62/// `Critical` and `Important` are **non-negotiable**: the scheduler never drops
63/// them to save frame time. Work that must be deterministic — physics in a
64/// simulation, or editor chrome once it owns a non-negotiable pass — lives here.
65/// Only `Optional` work is negotiable (the budget escape valve).
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
67pub enum AgentImportance {
68 /// Must execute — non-negotiable, never skipped. Skipping causes errors or corruption.
69 Critical,
70 /// Should execute — non-negotiable under normal budget pressure (not skipped today).
71 Important,
72 /// Nice to have — **negotiable**: the first (and currently only) work the
73 /// scheduler skips under budget pressure.
74 Optional,
75}
76
77impl AgentImportance {
78 /// Whether GORNA may skip this work under budget pressure. Only `Optional`
79 /// is negotiable; `Critical` and `Important` are non-negotiable (always run).
80 pub fn is_negotiable(self) -> bool {
81 matches!(self, AgentImportance::Optional)
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::AgentImportance;
88
89 #[test]
90 fn only_optional_is_negotiable() {
91 assert!(!AgentImportance::Critical.is_negotiable());
92 assert!(!AgentImportance::Important.is_negotiable());
93 assert!(AgentImportance::Optional.is_negotiable());
94 }
95}