pub trait Suggestor: Send + Sync {
// Required methods
fn name(&self) -> &str;
fn dependencies(&self) -> &[ContextKey];
fn accepts(&self, ctx: &dyn Context) -> bool;
fn execute<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 dyn Context,
) -> Pin<Box<dyn Future<Output = AgentEffect> + Send + 'async_trait>>
where 'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait;
}Expand description
The core suggestor contract.
Every suggestor in the Converge ecosystem implements this trait — whether it wraps an LLM, a policy engine, an optimizer, or a simple rule.
The engine calls accepts() to determine eligibility, then execute()
to collect effects. Effects are merged by the engine in deterministic
order (sorted by suggestor name).
§Async
execute() is async, allowing suggestors to call LLM providers, search
backends, and other I/O without blocking. The engine awaits each
suggestor and controls concurrency — suggestors don’t need to manage
their own parallelism.
§Thread Safety
Suggestors must be Send + Sync because the engine may execute eligible
suggestors concurrently in the future.
Required Methods§
Sourcefn name(&self) -> &str
fn name(&self) -> &str
Human-readable name, used for ordering, logging, and provenance.
Must be unique within a convergence run. The engine sorts suggestors by name to ensure deterministic merge order.
Sourcefn dependencies(&self) -> &[ContextKey]
fn dependencies(&self) -> &[ContextKey]
Context keys this suggestor reads from.
The engine uses this to determine when a suggestor becomes eligible: a suggestor is a candidate when at least one of its dependency keys has been modified since the last cycle.
Sourcefn accepts(&self, ctx: &dyn Context) -> bool
fn accepts(&self, ctx: &dyn Context) -> bool
Pure predicate: should this suggestor execute given the current context?
§Contract
- Must be pure: no side effects, no I/O, no state mutation.
- Must be deterministic: same context → same answer.
- Must check idempotency via context: look for your own
contributions in context (both
Proposalsand target key), not internal flags.
Sourcefn execute<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 dyn Context,
) -> Pin<Box<dyn Future<Output = AgentEffect> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn execute<'life0, 'life1, 'async_trait>(
&'life0 self,
ctx: &'life1 dyn Context,
) -> Pin<Box<dyn Future<Output = AgentEffect> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Produce effects given the current context.
§Contract
- Read-only: do not mutate context. Return effects instead.
- Effects are collected by the engine and merged after all eligible suggestors have executed.
- For LLM suggestors: emit
ProposedFacttoContextKey::Proposals, not directly to the target key.