pub trait Agent: Send + Sync {
// Required methods
fn id(&self) -> AgentId;
fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse;
fn apply_budget(&mut self, budget: ResourceBudget);
fn report_status(&self) -> AgentStatus;
fn execute(&mut self, context: &mut EngineContext<'_>);
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
// Provided methods
fn on_initialize(&mut self, _context: &mut EngineContext<'_>) { ... }
fn execution_timing(&self) -> ExecutionTiming { ... }
fn access(&self) -> AgentAccess { ... }
fn deck_writes(&self) -> Vec<TypeId> { ... }
}Expand description
The foundational interface for an Intelligent Subsystem Agent (ISA).
Each major subsystem (Rendering, Physics, etc.) implements this trait to participate in the engine’s dynamic resource negotiation (GORNA).
§Lifecycle
on_initialize(ctx)— called once after registration. The agent caches required services and initializes its lanes.execute(ctx)— called every frame. The agent selects the appropriate lanes based on the current GORNA budget and dispatches theirLane::execute().negotiate(request)/apply_budget(budget)— called by the GORNA arbitrator on the DCC background thread when the system re-evaluates strategy.report_status()— polled by GORNA for health monitoring.
An agent must contain no business logic beyond lane selection, budget
negotiation, and lane dispatch. All real work belongs in [Lane] implementations.
§Examples
A skeleton agent. Real agents cache their lanes in on_initialize and select
one per frame in execute based on the budget applied via apply_budget.
(Marked ignore because a compiling impl needs the GORNA request/response
types and an EngineContext from the running engine.)
use khora_core::agent::{Agent, ExecutionTiming};
use khora_core::control::gorna::{
AgentId, AgentStatus, NegotiationRequest, NegotiationResponse, ResourceBudget,
};
use khora_core::EngineContext;
use std::any::Any;
#[derive(Default)]
struct MyAgent;
impl Agent for MyAgent {
fn id(&self) -> AgentId { AgentId::Renderer }
fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse {
// Propose a strategy that fits `request`'s constraints.
NegotiationResponse::default()
}
fn apply_budget(&mut self, _budget: ResourceBudget) {
// Adjust quality / LOD to stay within the allocated budget.
}
fn report_status(&self) -> AgentStatus { AgentStatus::default() }
fn execute(&mut self, _ctx: &mut EngineContext<'_>) {
// Pick a lane for this frame and dispatch `Lane::execute`.
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}Required Methods§
Sourcefn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse
fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse
Negotiates with the DCC to determine the best execution strategy given the current global resource constraints and priorities.
Sourcefn apply_budget(&mut self, budget: ResourceBudget)
fn apply_budget(&mut self, budget: ResourceBudget)
Applies a resource budget issued by the DCC. The agent must adjust its internal logic (e.g., LOD, quality settings) to stay within the allocated limits.
Sourcefn report_status(&self) -> AgentStatus
fn report_status(&self) -> AgentStatus
Reports the current status and health of the agent.
Sourcefn execute(&mut self, context: &mut EngineContext<'_>)
fn execute(&mut self, context: &mut EngineContext<'_>)
Called every frame by the engine loop.
The agent selects the appropriate lanes based on the current GORNA
strategy, builds a LaneContext, and dispatches
Lane::execute() for each lane that should run.
Sourcefn as_any_mut(&mut self) -> &mut dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any
Allows mutable downcasting to concrete agent types.
Provided Methods§
Sourcefn on_initialize(&mut self, _context: &mut EngineContext<'_>)
fn on_initialize(&mut self, _context: &mut EngineContext<'_>)
Called once after the agent is registered with the DCC.
The agent should cache services from context.services, initialize
its lane registry, and prepare any persistent state.
Default implementation is a no-op.
Sourcefn execution_timing(&self) -> ExecutionTiming
fn execution_timing(&self) -> ExecutionTiming
Declares WHEN and HOW this agent should execute within the frame pipeline.
The Scheduler uses this information to filter agents by phase, order them by priority, and skip optional agents under budget pressure.
Default implementation returns a timing that allows execution in all phases
with Important priority.
Sourcefn access(&self) -> AgentAccess
fn access(&self) -> AgentAccess
Declares this agent’s data-access footprint during execute,
so the scheduler can decide whether it is safe to run concurrently with
other agents in the same phase.
Defaults to AgentAccess::Exclusive — the safe fallback: the agent is
assumed to need exclusive &mut World, so it runs serially. An agent
overrides this to AgentAccess::Isolated only if execute provably
touches no World and no shared mutable engine resource (it reads the
LaneBus and writes solely its own OutputDeck).
Sourcefn deck_writes(&self) -> Vec<TypeId>
fn deck_writes(&self) -> Vec<TypeId>
Declares the OutputDeck slot types this agent
writes during execute, by TypeId.
The scheduler uses this at wave-formation time: two concurrency-eligible agents grouped into the same wave write into private deck shards that are folded back together afterwards, so they must write disjoint slot types. Declaring the written slots lets the scheduler catch a collision when the wave is built — naming the offending agents — instead of only discovering it defensively during the shard merge.
Defaults to empty: an agent that writes no deck slot (or only runs
Exclusive, i.e. never in a concurrent wave) need not override it.